Laravel 8 eager loading, how to access subquery fields - laravel

This is working:
$clients = Client::with([
'contacts' => function ($query) {
$query
->select('client_id', 'first_name', 'last_name')
->where('contact_type_id', '=', 1);
}])
->orderBy('client_name')
->get(['id', 'client_name', 'city', 'state']);
dd($clients);
However, I'm unsure of how to access first_name and last_name on the subquery. They're showing up in the "relations" object in the dump, but in my mind I'm envisioning a dataset that I would access like,
$client->first_name, etc.
When I try to add the fields to the get() method at the end, it doesn't recognize them, so I'm doing something wrong, or I need to access the subquery fields differently.

When you are eager loading the relationships, you are just preloading them.
If your relationship is defined as hasMany (which seems to be the case here, a Client hasMany Contact), then you'll always get a collection from eager loading.
If you defined another relationship in your model in order to get only one result, for instance:
public function contact()
{
return $this->hasOne(Contact::class)->where('contact_type_id', 1)->latest('id');
}
Your new relationship contact would return only one result:
$clients = Client::with('contact')
->orderBy('client_name')
->get(['id', 'client_name', 'city', 'state']);
foreach($clients as $client){
dd($client->contact->first_name);
}
// each Client of $clients would have a ->contact relationship

You should add field id to select() method in subquery and field contact_id to get() method
$clients = Client::with(['contacts' => function ($query) {
$query->select('id', 'client_id', 'first_name', 'last_name')->where('contact_type_id', '=', 1);
}])
->orderBy('client_name')
->get(['id', 'client_name', 'city', 'state', 'contact_id']);
foreach ($clients as $key => $client) {
dd($client->contacts->first_name);
// Or
dd($client->contacts()->first()->first_name);
}

Related

output category with child categories and posts and pagination

I want to get category with all children subcategories and posts where id = category_id.
Posts should be paginated.
In category model I have 2 relations.
public function children() {
return $this->hasMany(self::class, 'parent_id');
}
public function posts() {
return $this->hasMany(Post::class, 'category_id');
}
In controller I have this function
public function getCategoryWithPaginatedPosts($slug, $perPage = null)
{
$columns = ['id', 'parent_id', 'title', 'slug', 'description', 'image'];
$postsColumns = ['category_id','title','text'];
$result = Category::whereSlug($slug)
->select($columns)
->with('children')
->with(['items' => function ($q) use ($postColumns) {
$q->wherePublished(true)
->select($postColumns)
->orderBy('id', 'ASC')
->paginate();
}])
->first();
}
Pagination doesn't work.
I just see that number of items is equal to $perPage parameter (and I have more items), but I don't see paginator inside dd($result->items)
It works like that, though I believe it is not the best way to do that.
So I can do it in few steps.
In first step I retrieve all data from DB and convert models to array, because I don't need models on webpage and I suppose it works faster like that. I would use ->toBase() if it could take mutators and relations from the model.
Second step I convert array into stdClass, because it is more comfortable in blade to work with object rather than with array.
Third step is to paginate items with mypaginate function (manual paginator in AppService Provider).
public function getCategoryWithPaginatedPosts($slug, $perPage = null)
{
$columns = ['id', 'parent_id', 'title', 'slug', 'description', 'image'];
$postsColumns = ['category_id','title','text'];
$result = Category::whereSlug($slug)
->select($columns)
->with('children')
->with(['items' => function ($q) use ($postColumns) {
$q->wherePublished(true)
->select($postColumns)
->orderBy('id', 'ASC');
}])
->first()
->toArray();
$result = Arr::arrayToObject($result);
$result->items = collect($result->items)->mypaginate($perPage);
return $result;
}
you should not use ->first() after ->paginate(), change something like this,
$result = Category::whereSlug($slug)
->select($columns)
->with('children')
->with(['items' => function ($q) use ($postColumns) {
$q->wherePublished(true)
->select($postColumns)
->orderBy('id', 'ASC')
}])
->paginate(20);

how to select specific fields from the tables in laravel

This works but it gives all the fields of account table but only few are required.
$data = Loan::select('*')
->with([
'member' => function ($query) {
$query->addSelect('id', 'name');
},
'member.account'
])
->get();
This gives account: null
$data = Loan::select('*')
->with([
'member' => function ($query) {
$query->addSelect('id', 'name');
},
'member.account'=> function ($query) {
$query->addSelect('id', 'account'); // if this line is commented, all the fields are returned but I just need few fields.
}
])
->get();
Member model:
public function account()
{
return $this->hasOne(Account::class);
}
Account model:
public function member()
{
return $this->belongsTo(Member::class);
}
How can I get only the required fields of account table?
You can define the relationship and set it to give you specific columns like this.
on Member model
public function account()
{
return $this->hasOne(Account::class)->select(['id', 'account']);
}
You can try to define required fields in with:
$data = Loan::select('*')
->with([
'member' => function ($query) {
$query->addSelect('id', 'name');
},
'member.account:id,number'
])
->get();
Your are close you just need to select the foreign key column that points to your account model , I guess it would be account_id, So you basically need to select 3 columns from your table which are select(['id', 'name','account_id'])
$data = Loan::with(['member' => function ($query) {
$query->select(['id', 'name','account_id'])
->with(['account'=> function($query){
$query->select(['id','name']);
}]);
}])
->get();
So if you have another nested relation account -> type and you need specific columns from type so you need to select the foreign key column from accounts models as
with(['account'=> function($query){
$query->select(['id','name','type_id'])
->with(['type'=> function($query){
$query->select(['id','name']);
}]);
}]);

Laravel - Get array with relationship

I have an ajax call that returns an array:
$reports = Report::where('submission_id', $submissionID)
->where('status', 'pending')
->get(['description','rule']);
return [
'message' => 'Success.',
'reports' => $reports,
];
From this array, I only want to return the fields 'description' and 'rule'. However I also want to return the owner() relationship from the Report model. How could I do this? Do I have to load the relationship and do some kind of array push, or is there a more elegant solution?
You can use with() to eager load related model
$reports = Report::with('owner')
->where('submission_id', $submissionID)
->where('status', 'pending')
->get(['id','description','rule']);
Note you need to include id in get() from report model to map (owner) related model
you will have probably one to many relationship with Reports and owners table like below
Report Model
public function owner() {
return $this->belongsTo('App\Owner');
}
Owner Model
public function reports() {
return $this->hasMany('App\Report');
}
your controller code
$reports = Report::with('owner')->
where('submission_id', $submissionID)->where('status', 'pending')->get()
return [
'message' => 'Success.',
'reports' => $reports,
];
This is what I ended up going with:
$reports = Report::
with(['owner' => function($q)
{
$q->select('username', 'id');
}])
->where('submission_id', $submissionID)
->where('status', 'pending')
->select('description', 'rule','created_by')
->get();
The other answers were right, I needed to load in the ID of the user. But I had to use a function for it to work.

Laravel eager not working with select function

If I use select function the data disappears
$regions = Region::select('id', 'place_id', 'formatted_address')
->where('lang', $lang)
->with(['areas' => function($query) {
return $query
->select('id', 'place_id', 'formatted_address') // << the problem is here
->whereNotNull('area1')->whereNull('locality')->whereNull('area2');
}])
->get();
if you remove the select function, the areas field is filled with data
Relations:
$this->hasMany(Region::class, 'country', 'formatted_address');
The request must contain fields that participate in the relationship
$this->hasMany(Region::class, 'country', 'formatted_address');
->select('place_id', 'country', 'formatted_address')

Ordering Results from Related Tables in Eloquent and the Session

I have the following in my Event model in Laravel 4. The reason I am using QueryBuilder and not Eloquent is I need to have links in each column header in my results table in my view, that when clicked, order the results in asc or desc based on that column.
The issue I'm having is, if I use Eloquent, it won't work as most of the data is pulled through via relationships to other tables, so Eloquent can't find the required columns/fields.
public static function getEvents($perPage = 10)
{
$order = Session::get('event.order', 'start_date.desc');
$order = explode('.', $order);
$columns = array(
'events.id as id',
'title',
'locations.city as city',
'suppliers.name as supplier_name',
'venues.name as venue_name',
'start_date',
'courses.price as course_price',
'type',
'status',
'max_delegates as availability',
'tutors.first_name as tutor_first_name',
'tutors.last_name as tutor_last_name',
'contacts.first_name as d_first_name'
);
$events = DB::table('events')
->leftJoin('courses', 'course_id', '=', 'courses.id')
->leftJoin('suppliers', 'supplier_id', '=', 'suppliers.id')
->leftJoin('locations', 'location_id', '=', 'locations.id')
->leftJoin('venues', 'venue_id', '=', 'venues.id')
->leftJoin('event_types', 'event_type_id', '=', 'event_types.id')
->leftJoin('event_statuses', 'event_status_id', '=', 'event_statuses.id')
->leftJoin('tutors', 'tutor_id', '=', 'tutors.id')
->leftJoin('delegate_event', 'delegate_event.event_id', '=', 'events.id')
->leftJoin('delegates', 'delegates.id', '=', 'delegate_event.delegate_id')
->leftJoin('contacts', 'delegates.contact_id', '=', 'contacts.id')->groupBy('events.id')
->select($columns)
->orderBy($order[0], $order[1])
->paginate($perPage);
return $events;
}
If you look at my EventsController at the getOrder method:
public function getOrder($order)
{
Session::put('event.order', $order);
return Redirect::back();
}
You can see I am storing the order in the session and then in my model using that to sort the order of results.
Is there a way to do this in Eloquent the way I need to?
You know you can just add a join() to eloquent right?
So, as an example, if you have users and each user has some blog posts (i.e. user has_many blogs), you can output an eloquent model showing username and blog title, ordered by blog title, as follows:
user::join('blogs', 'blogs.id','=','users.blog_id')
->order_by('blogs.title', 'asc')
->select(array('users.username', 'blogs.title'))
->paginate(10);

Resources