Laravel Retrieving Specific fields from nested Relationships - laravel-5

I have some nested relationships,
Following are my models:
Post, Thread and BookPage.
and their relationships:
Post
class Post extends Model
{
public function thread(){
return $this->belongsTo('App\Thread');
}
}
Thread
class Thread extends Model
{
public function bookPage(){
return $this->belongsTo('App\BookPage');
}
public function posts(){
return $this->hasMany('App\Post');
}
}
BookPage
class BookPage extends Model
{
public function threads(){
return $this->hasMany(App\Threads);
}
}
now i wanted to get posts with the bookpages and threads but only some fields let say only thread name and bookpage title
I have read about retrieving relationships and i am doing exactly like explained in the docs but here is my problem :
When i am retrieving posts with relations without specifying fields i am getting results fine:
Query:
$posts = Post::with('thread.bookpage:id,title)->get();
return $posts->toArray();
Result:
but when i specify fields for both i.e fields for bookpage and also fields for thread i am getting bookpage as null.
I am able to get specific fields for one relation only either for bookpage or either for thread.
I have tried to query in the following ways:
$posts = Post::with('thread.bookpage:id, ...')->get();
//thread:all fields, bookpage:specified fields
$posts = Post::with('thread.bookpage', 'thread:id, ...')->get();
//thread:specified fields, bookpage:bull
Here is the result if i tried to specify fields for both:
so how can i get the specific fields only from the relation and its nested relation ?

You have to select all the foreign keys (here: thread.book_page_id) that Laravel requires to match the eager loading results to their parents:
$posts = Post::with('thread:id,book_page_id', 'thread.bookpage:id,title')->get();

Related

Laravel eloquent for four tables

I'm new to Laravel. I am developing a project. and in this project I have 4 tables related to each other
-Users
-Orders
-OrderParcels
-Situations
When listing the parcels of an order, I want to get the information of that order only once, the user information of that order once again, and list the parcels as a table under it. so far everything ok. but I also want to display the status of the parcels listed in the table as names. I couldn't add the 4th table to the query. do you have a suggestion? I'm putting pictures that explain the structure below.
My current working code is
$orderParcels = Orders::whereId($id)
->with('parcels')
->with('users:id,name')
->first();
and my 'orders' model has method
public function parcels(){
return $this->hasMany(OrderParcels::class);
}
public function users(){
return $this->hasOne(User::class,'id','affixer_id');
}
Note[edit]: I already know how to connect like this
$orderParcels = DB::table('order_parcels as op')
->leftjoin('orders as o','op.orders_id','o.id')
->leftjoin('users as u','o.affixer_id','u.id')
->leftjoin('situations as s','op.status','s.id')
->select('op.*','o.*','u.name','s.situations_name')
->where('op.orders_id',$id)->get();
but this is not working for me, for each parcels record it returns me orders and user info. I want once orders info and once user info.
Laravel provides an elegant way to manage relations between models. In your situation, the first step is to create all relations described in your schema :
1. Model Order
class User extends Model {
public function parcels()
{
return $this->hasMany(OrderParcels::class);
}
public function users()
{
return $this->hasOne(User::class,'id','affixer_id');
}
}
2. Model Parcel
class Parcel extends Model {
public function situations()
{
return $this->hasOne(Situation::class, ...);
}
}
Then, you can retrieve all desired informations simply like this :
// Retrieve all users of an order
$users = $order->users; // You get a Collection of User instances
// Retrieve all parcels of an order
$parcels = $order->parcels; // You get a Collection of User instances
// Retrieve the situation for a parcel
$situations = $parcel->situations // You get Situation instance
How it works ?
When you add a relation on your model, you can retrieve the result of this relation by using the property with the same name of the method. Laravel will automatically provide you those properties ! (e.g: parcels() method in your Order Model will generate $order->parcels property.
To finish, in this situation where you have nested relations (as describe in your schema), you should use with() method of your model to eager load all the nested relation of order model like this :
$orders = Orders::with(['users', 'parcels', 'parcels.situations'])->find($id)
I encourage you to read those stubs of Laravel documentation :
Define model relations
Eager loading
Laravel Collection
Good luck !
Use join to make a perfect relations between tables.
$output = Orders::join('users', 'users.id', '=', 'orders.user_id')
->join('order_parcels', 'order_parcels.id', '=', 'orders.parcel_id')
->join('situations', 'situation.id', '=', 'order_parcels.situation_id')
->select([
'orders.id AS order_id',
'users.id AS user_id',
'order.parcels.id AS parcel_id',
'and so on'
])
->where('some row', '=', 'some row or variable')->get();

Laravel: With and whereHas to filter second relation hasOne

i'm trying to filter the table using "with" and "whereHas" for the relation and have it follow a second second relation.
Is it possible to do it with "with" or would it only be possible with "Joins"?
Ticket >> StatusHistory (Last record) >> StatusName = 'new'
ticket
-id
-name
status_history
- ticket_id
- status_name_id
- timestamps
status_names
- id
- name (new, close, paused)
<?
class Ticket extends Model
{
public function latestStatus()
{
return $this->hasOne(StatusHistory::class, 'ticket_id', 'id')->latest();
}
class StatusHistory extends Model
{
public function statusName()
{
return $this->hasOne(StatusName::class, 'id', 'status_name_id');
}
This usually works well if there is only one Status history record, but if there are more, it returns values that should not be there.
example: ticket_id 1 has in history first status new and them status paused
With this sentence he returned the ticket to me even so he no longer has the last status in "new".
Ticket::with('latestStatus')
->whereHas('latestStatus.statusName', function($q){
$q->where('name', 'new');
})
According to the documentation (https://laravel.com/docs/8.x/eloquent-relationships#constraining-eager-loads) it is possible. It would look like this:
Ticket::with(['latestStatus' => function($q){
$q->where('name', 'new');
}])->get();
So that the subquery is linked to the relation you are trying to load
To access the first relationship you just use:
$ticket = Ticket::find($id);
$ticket->latestStatus
By having a "hasOne" relationship established, this will return the related record, which from what I see also has a hasOne relationship, so you can do the following:
$ticket->latestStatus->statusName
In this way, you are accessing the second relationship and working it as usual.
However, this is not the only way, as Laravel also offers access to chained relationships through the "has-one-through" method, which according to the documentation is defined as:
"...this relationship indicates that the declaring model can be matched with one instance of another model by proceeding through a third model."
class Ticket extends Model{
public function statusName()
{
return $this->hasOneThrough(StatusName::class, StatusHistory::class);
}
}
Take into account that for this you must follow the conventions established by Laravel. I leave here the related links, I am sure they will be very helpful. Greetings.
Relationships: one-to-one
Relationships: has-one-through

How to use eloquent to get a value from two Has Many Relations

I have an issue where a profile can have many campaigns and also many locations.
The campaigns are linked via a pivot table but my goal is just to return all of the location ids.
Profile:
public function campaigns() {
return $this->hasMany('App\Models\Campaign', 'profile_id', 'id');
}
Campaign:
public function locations() {
return $this->belongsToMany('App\Models\Location')->withPivot('campaign_id', 'location_id');
}
Currently I am solving this by doing
$campaigns = $profile->campaigns;
[Doing a nested foreach loop and placing each ID into the array]
How would I get this via a query?
I've tried
$campaigns = $profile->campaigns()
->with('locations')
->get()
->pluck('location.id');
Well, your goal is to get information from the location, so I'd start your query with that. You can condition your query based on the relationships using the whereHas() method.
This assumes your Location has the campaigns relationship defined, and your Campaign has the profile relationship defined.
$ids = Location::whereHas('campaigns.profile', function ($query) use ($profile) {
return $query->where('id', $profile->id);
})->pluck('id');
You can read more about querying by relationships in the documentation here.

Nested With() in Laravel Model Collection Return

I have a collection that is returned as such:
$scheduleLoads = Load::with('shipment','driver','tractor');
Now, my question is related to the with issue - is there a way to add the relationships of these relationships into my returned collection?
For example:
In the Load model I have the following relationship to shipment:
public function shipment(){
return $this->belongsTo(shipment::class, 'shipmentID');
}
In the shipment model I have the following relationship:
public function shiptoAccount(){
return $this->belongsTo('App\Customer', 'ship_to');
}
Is there a way to include the shiptoAccount return of the shipment associated with the Loads collections?
Use the "dot" syntax (documentation):
$scheduleLoads = Load::with('shipment.shiptoAccount', 'driver', 'tractor');

Laravel 4.2 Eloquent query by relationship column value

Good day to you all...
I'm trying to access a collection based on a column in a related table within Eloquent (Laravel 4.2).
I have the following tables:
tags:
(int) id
(string) name
tag_usage:
(int) id
(string) model (the name of the model that is allowed to use the tag)
tag_tag_usage: (pivot)
(int) id
(int) tag_id
(int) tag_usage_id
I also have a taggables (polymorphic to store tags for multiple models) table which I believe is out of scope here as I only want to retrieve the tags that am allowed to use for each model.
My tag model has the relationship
public function usage()
{
return $this->belongsToMany('TagUsage');
}
and the TagUsage model has
public function tags() {
return $this->belongsToMany('Tag');
}
Now, what I want to do is return the tags that ONLY have a specific usage, some pseudo code would be
get_tags->where(tag_usage.model = modelname)
which would return only a subset of the tags.
Tried a few things with no success so over to the many fine brains available here.
Many thanks.
You need to use whereHas in the following way:
$tags = Tag::whereHas('usage', function($q)
{
$q->whereModel('modelname');
})->get();

Resources