Is it possible to join eloquent relationship query? - laravel

I have a users, departments, and positions table.
I want to select one department with all the users in that department with their position name.
Currently, I have this.
$department = DepartmentView::with('users')
->findOrFail($departmentId);
It returns me a department with users, but I want to join the users with positions table so I can also get the position name. (user table only has position id)

Assuming you have your relationships setup properly in your User model and the relationship is called position, it should be like this:
$department = DepartmentView::with('users.position')
->findOrFail($departmentId);
Look at eager loading -> nested eager loading.

You can do
$department = DepartmentView::with('users.position')->findOrFail($departmentId)
position is referred to the relationship set on the User model to get the user position.

Related

Eloquent relations from multiple joined tables

Let's say we have a table users and we are left joining multiple tables to it.
$users = User::query()
->select('users.id', 'bananas.id as banana_id', 'dogs.id as dog_id')
->leftJoin('bananas', 'banana.user_id', '=', 'users.id')
->unionAll($usersWithDogs) // a similar query with a left join on `dogs`
->orderByDesc('users.created_at')
->paginate(...);
We end up with a collection of User models with attributes id, dog_id, banana_id.
Now imagine we want to eager load these, but the eloquent relations are based on the one to many relationships, $user->dogs, $user->bananas.
Trying to find a solution that will do all the following:
a) not break pagination
b) allow ordering on the user table
c) allow eager loading
d) use clean code
e) end up with a collection of users
Brainstorming so far has led to the following options:
A union of bananas and dogs, eager load the user relation, then invert the collection (messy code)
Dynamic relationships created on User, possibly with a macro on \Illuminate\Database\Eloquent\Builder. Maybe by leveraging Model::resolveRelationUsing()
Manual eager loading with a union select with a left join to each table in each arm of the union, then a whereIn() to get the related records
Restructure the relations so that there is a polymorphic many to many relationship between users and other entities
e.g.
user_id | model_type | model_id
1 | App\Models\Banana | 2
1 | App\Models\Dog | 5
Maybe I'm missing something obvious...?

Why Laravel does not make lots of queries when using exists on relations?

I have such entities as:
Company
Person
Company hasMany Persons. So in the persons table there is company_id column.
I return company list, which I pass to CompanyResource. There I return has_persons => $this->persons()->exists() value.
Then I checked the result of DB::getQueryLog() and I found out that there is only one SQL query, which does not have count or anything like that.
In order to count how many persons a company has, Laravel should make one query per company, shouldn't it? Like select count (*) from persons where company_id = 5 for example
try this
$this->persons->count()

Query on eloquent relationship

I have Book and Store models which have belongsToMany relationship.
In Book.php
public function stores(){
return $this->belongsToMany('App\Store')->withPivot('qty');
}
In Store.php
public function books(){
return $this->belongsToMany('App\Book')->withPivot('qty');
}
Now I just want to know the total number of books in store A and B together. How can I do it with eloquent ORM? I can get all books belonging to store A and B using whereHas but cannot go further to aggregate the qty field in the pivot table.
So you want the total qty of books by combination of store id and book id
The way you've described your DB structure, it looks like your pivot table has exactly these columns: book_id, store_id and qty
So all you really need to do is:
DB::table('book_stores')->get()

Laravel Eloquent select function cause empty relation

Following is my query
$user = User::select(['uuid','name','about'])->with(['education','work'])->first();
this returns empty data for relationship education and work,
but if I remove select function from query I am getting data in relationship and it also returns all columns of user table which I don't want.
how can solve this problem
The problem is that relationships (with(...)) execute an additional query to get the related results. Let's say you have one to many relationship where users have many works. User::with('work')->find(1) will then execute these 2 queries:
select user where id = 1 and select works where user_id = 1.
So basically in order to be able to execute the second query (fetch relationship data) you need to include id (or whichever column you're referencing) in you select statement.
Fix:
$user = User::select(['uuid','name','about', 'id'])->with(['education','work'])->first();
Same principle in different forms applies to all relationships. For example in the inverse of hasMany which is belongsTo you would need to select the foreign key (for example user_id).

Newest items and GROUP By with Eloquent

I have the following prices-table:
shop_id (int)
product_id (int)
price (float)
created (DateTime)
Every hour a cronjob checks the shops and inserts new entries (current prices) into these price-table.
Now I want to display the newest price for a product. I have to GROUP BY the shop_id because I only want one price per shop but I only want the newest entry (created).
Can I solve this with Eloquent Query-Builder or do I have to use raw SQL? Is it possible to pass the result of a raw SQL-query into a model if the columns are the same?
You can try it as:
Price::select('*', DB::raw('MAX(created_at) as max_created_at'))
->groupBy('shop_id')
->get()
Assuming model name is Price
Eloquent (purist) approach:
Price::orderBy('created', 'desc')->groupBy('shop_id')
->get('shop_id', 'price');
References:
https://laravel.com/api/5.3/Illuminate/Database/Query/Builder.html#method_orderBy
https://laravel.com/api/5.3/Illuminate/Database/Query/Builder.html#method_groupBy
https://laravel.com/api/5.3/Illuminate/Database/Query/Builder.html#method_get
*untested though
Q: Is it possible to pass the result of a raw SQL-query into a model if the columns are the same?
A: you could pass it to Model's contructor - but it might need model's field to be fillable - or hydrate a model. Alternatively, just access it like an keyed-array, ie. $something[0]['price'] <-- assuming an array of prices with price column.
I solved the problem without QueryBuilder. Instead I use a raw SQL-statement and generating the models with the hydrateRaw()-function of the Model-class.
$prices = Price::hydrateRaw( 'SELECT p.*
FROM prices p
INNER JOIN (
SELECT shop_id, max(created_at) AS max_ca
FROM prices p1
GROUP BY shop_id
) m ON p.shop_id = m.shop_id AND p.created_at = m.max_ca');

Resources