I'm working with L5 and elequent
My table structure is..
users
id
other field
auctions
id
other field
lots
id
auction_id
other field
lot_user
lot_id
user_id
I want to find auctions for a user.
How can i do this?
$user = User::find($id);
$auctions = $user->auctions();
I have got an idea to do this with eloquent..
$auctions = $user->lots()
->join('auctions','auctions.id','=','lots.id')
->select(['auctions.*'])
->get();
I'm not sure Eloquent is going to be very efficient here, but you could do something like :
In your User(s) class, you need to define a many-many relationship like :
public function lots()
{
return $this->belongsToMany('App\Lot');
}
In your Lot(s) class, you need to define the inverse of a one-to-many relationship like:
public function auctions()
{
return $this->belongsTo('App\Auction')
}
Then, to get Lots for a user, you'd do something like :
$user->lots();
To get auctions, you'd need to loop over lots and call $lot->auctions() for each one, and then filter by id to get the unique auctions.
This is a case where it would probably be easier to use the DB facade to built a query instead of just trying to use Eloquent.
About DB facade.
Raw query will looks like this:
SELECT * FROM auctions AS a
INNER JOIN lots AS l ON (l.auction_id = a.id)
INNER JOIN lot_user AS lu ON (lu.lot_id = l.id AND lu.user_id = $findUserId)
GROUP BY a.id
And using query-builder you can do it like this:
DB::table('auctions')
->join('lots', 'lots.auction_id', '=', 'auctions.id')
->join('lot_user', function ($join) {
$join->on('lot_user.lot_id', '=', 'lots.id')
->where('lot_user.user_id', '=', $findUserId);
})
->groupBy('auctions.id')
->get();
Related
I have three models with the following hierarchy :
User
id
....some other properties
Journey
id
user_id
budget
....some other properties
Confirmation
id
journey_id
user_id
....some other properties
I have a HasMany from User to Journey, a HasMany from Journey to Confirmation.
I want to get the sum for a column of the journeys table by going through the confirmations table but I cannot create an intermediate HasManyThrough relation between User and Journey by using Confirmation.
I have tried to do
public function journeysMade(): HasManyThrough
{
return $this->hasManyThrough(Journey::class, Confirmation::class);
}
// And after,
User::with(...)->withSum('journeysMade','budget')
But it was not possible because the relations are not adapted.
With hindsight, the sql query I want to translate would look like
select coalesce(sum(journeys.budget), 0) as income
from journeys
inner join confirmations c on journeys.id = c.journey_id
where c.user_id = ? and c.status = 'finalized';
How can I implement this query considering how I will use my query builder :
$driversQueryBuilder = User::with(['profile', 'addresses']); // Here
$pageSize = $request->input('pageSize', self::DEFAULT_PAGE_SIZE);
$pageNumber = $request->input('pageNumber', self::DEFAULT_PAGE_NUMBER);
$driversPaginator = (new UserFilterService($driversQueryBuilder))
->withStatus(Profile::STATUS_DRIVER)
->withCountry($request->input('country'))
->withSex($request->input('sex'))
->withActive($request->has('active') ? $request->boolean('active') : null)
->get()
->paginate(perPage: $pageSize, page: $pageNumber);
return response()->json(['data' => $driversPaginator]);
The reason why I want to get a builder is because UserFilterService expects a Illuminate\Database\Eloquent\Builder.
Do you have any idea about how I can solve this problem ?
Not 100% sure what exactly you want to sum, but I think you need the following query
$user->whereHas('journeys', function($query) {
$query->whereHas('confirmations', function($subQuery) {
$subQuery->sum('budget);
}
});
If you the above query isn't summing the budget you need, you just add another layer of abstraction with whereHas methods to get exactly what you need. Hope this helps!
EDIT:
$user->whereHas('confirmations', function($q) {
$q->withSum('journeys', 'budget')->journeys_sum_budget;
}
How to order laravel eloquent query using parent model?
I mean I have an eloquent query where I want to order the query by its parent without using join relationship?
I used whereHas and order by on it, but did not work.
Here is a sample of my code:
$query = Post::whereHas('users')->orderBy('users.created_at')->get();
If you want to order Post by a column in user you have to do a join in some way unless you sort after you retrieve the result so either:
$query = Post::select('posts.*')
->join('users', 'users.id', 'posts.user_id')
->orderBy('users.created_at')->get();
Note that whereHas is not needed anymore because the join (which is an inner join by default) will only result in posts that have a user.
Alternatively you can do:
$query = Post::has('users')
->with('users')
->get()
->sortBy(function ($post) { return $post->users->created_at; });
The reason is that eloquent relationships are queried in a separate query from the one that gets the parent model so you can't use relationship columns during that query.
I have no clue why you wanted to order Posts based on their User's created_at field. Perhaps, a different angle to the problem is needed - like accessing the Post from User instead.
That being said, an orderBy() can accept a closure as parameter which will create a subquery then, you can pair it with whereRaw() to somewhat circumvent Eloquent and QueryBuilder limitation*.
Post::orderBy(function($q) {
return $q->from('users')
->whereRaw('`users`.id = `posts`.id')
->select('created_at');
})
->get();
It should generate the following query:
select *
from `posts`
order by (
select `created_at`
from `users`
where `users`.id = `posts`.id
) asc
A join might serve you better, but there are many ways to build queries.
*As far as I know, the subquery can't be made to be aware of the parent query fields
You can simply orderBy in your Post model.
public function users(){
return $this->belongsTo(User::class, "user_id")->orderByDesc('created_at');
}
I hope this helps you.
You can try
Post::query()
->has('users')
->orderBy(
User::select('created_at')
->whereColumn('id', 'posts.user_id')
->orderBy('created_at')
)
->get();
The sql generated would be like
select * from `posts`
where exists (select * from `users` where `posts`.`user_id` = `users`.`id`)
order by (select `created_at` from `users` where `id` = `posts`.`user_id` order by `created_at` asc) asc
But I guess join would be a simpler approach for this use case.
Laravel Docs - Eloquent - Subquery Ordering
I'm using luman and Database Query Builder to fetch full user info from database.
First, Please Take a lock at my database structure:
I have a table called users and a series of other tables that are related to user groups (Ex: secretaries, patients, doctors and admins) which stores additional information about the users.
Also To determine user access, I have a level column on user table which can have one of this value as enum: 'admin', 'doctor', 'secretary', 'patient'.
So, I want to get this information using one query by join and select.
My training code is something like this:
$userInfo = User::where("userID", $userID)
->limit(1)
->join('[GROUP_TABLE_NAME]', function ($join) {
$join->on('user.userID', '=', '[GROUP_TABLE_NAME]' .'.'.
'[GROUP_NAME]' . 'ID');
})
->get();
The GROUP_NAME comes from level column on user table and the GROUP_TABLE_NAME can be built based on the GROUP_NAME value(Ex: ['secretary' => 'secretaries' , 'patient' => 'patients' , ...]).
Any idea to handle the join structure using laravel query builder?
First you should be aware of the fact that this code architecture is not convenient and not easy to understand for other developers.
SQL
You can achieve your goal by using union and join.
Just convert this query for laravel builder or use it directly with DB::statement: select users.*, infos.info from users left join (( select secretaries.* from secretaries ) UNION (select doctors.* from doctors)) infos ON users.id = infos.user_id where users.id=?.
But
The easiest way to do it is to fetch info in two queries, both indexed and fast: user from users by primary key and then info by indexed field user_id in it. Create Doctorinfo, Admininfo models and correspondent migrations. So user class can be smth like this:
public function getInfo() {
switch($this->level) {
'doctor':
return $this->doctorinfo;
...
}
}
private function doctorinfo() {
$this->hasOne('App\Doctorinfo');
}
Builder
You can use left join to join all sub tables as well. The following builder selects info column.
User::where("userID", $userID)->limit(1)
->leftJoin('patients', 'users.id', '=', 'patients.user_id')
->leftJoin('doctors', 'users.id', '=', 'doctors.user_id')
->leftJoin('admins', 'users.id', '=', 'admins.user_id')
->select('users.*', DB::raw('IF(users.level="admin", admins.info, (IF users.level="doctors", doctors.info, patients.info))'))
I am learning relationships in Laravel php framework and I am trying to build this query
SELECT * FROM users u INNER JOIN link_to_stores lts ON u.id=lts.user_id INNER JOIN stores s ON lts.store_id=s.store_id WHERE lts.privilege = 'Owner'
I built this in Model
Link_to_store.php
public function store()
{
return $this->belongsTo('App\Store');
}
public function user()
{
return $this->belongsTo('App\User');
}
User.php
public function store_links()
{
return $this->hasMany('App\Link_to_store');
}
Store.php
public function user_links()
{
return $this->hasMany('App\Link_to_store');
}
I tried this query but this only joins user and link_to_store table
$personal_stores = Auth::user()->store_links->where('privilege','=','Owner');
Now I am confused how to join store table too. Can anyone help with this?
Schema is like this
Stores Table
store_id store_name
Users Table
id name
Link_to_stores Table
id store_id user_id privilege
I suppose store_links is actually a pivot table. In this case, you can use belongsToMany(), this will automatically take care of the pivot table.
To do this, in your User model you change the store function to this:
function stores() {
return $this->belongsToMany('App\Store', 'store_links', 'user_id', 'store_id')->withPivot('privilege');
}
Because the primary key of stores is not id, you will have to define this in you Store model with the following line:
protected $primaryKey = 'store_id';
Now to get the stores for a user, you simply call
$stores = Auth::user->stores()->wherePivot('privilege', 'Owner')->get();
I am learning relationships in Laravel php framework and I am trying to build this query
SELECT * FROM users u INNER JOIN link_to_stores lts ON u.id=lts.user_id INNER JOIN stores s ON lts.store_id=s.store_id WHERE lts.privilege = 'Owner'
You are trying to do a join here. You can do a join like this:
$stores = User::join('link_to_stores as lts', 'users.id', '=', 'lts.user_id')->join('stores as s', 'lts.store_id', '=', 's.id')->where('lts.privilege', 'Owner')->get();
But like Jerodev pointed out, it seems like Many to Many relationship might make more sense in your case. The difference is that relationship will actually execute 2 queries (1 for original model, 1 for relationship). It will then attach the related models to the original model (which is extremely handy).
How to query with Eloquent, all users without a certain type of certificate?
Laravel 4
I've got 2 tables:
users table:
->id
->name
certificats table:
->id
->user_id
->certificate_type
I`m struggling with this for hours now. Last thing i tried was:
$users = User::with(array('certificate' => function($query)
{
$query->where('type','!=','SL');
}))->get();
This gives me all the users, but i was trying to get all the users without certificate type 'SL'.
-- edit:
Spencer7593's raw query below works. But i`m not getting the eloquent query to work.
SELECT u.*
FROM users u
LEFT
JOIN certificates c
ON c.user_id = u.id
AND c.type = 'SL'
WHERE c.user_id IS NULL
The relationship:
public function certificate(){
return $this->hasMany('Certificate');
}
public function certificate(){
return $this->belongsTo('User');
}
The SQL to get the result set you want is fairly simple.
SELECT u.*
FROM users u
LEFT
JOIN certificates c
ON c.user_id = u.id
AND c.type = 'SL'
WHERE c.user_id IS NULL
That's a familiar pattern called an "anti-join". Basically, it's a LEFT JOIN look for matching rows, along with rows from users that don't have a match, and then filter out all rows that did get a match, and we're left with rows from users that don't have match.
The trick is going to be getting Eloquent to generate that SQL for you. To get Eloquent to do that, you need to tell eloquent to do a LEFT JOIN, and add a WHERE clause,
maybe something like this would be close:
->left_join('certificate AS c', function($join){
$join->on('c.user_id','=','user.id');
$join->and_on('c.type','=','SL');
})
->where_null('c.user_id')
FOLLOWUP
(For the benefit of those who might not read the comments)
Klass Terst (OP), reports syntax problems in the attempt at Eloquent (in the answer above): left_join needed to be replaced with leftJoin, and the and_on wasn't recognized. (The latter may have been my invention, based on the convention used in with where, and_where, or_where.)
$users = DB::table('users')
->select('users.id','users.name')
->leftJoin('certificate AS c', function($join){
$join->on('c.user_id','=','user.id');
$join->on('c.type','=','SL');
})
->where_null('c.user_id');
I believe the problem is in your relationships.
Models
class User extends Eloquent
{
public function certificates()
{
return $this->hasMany('Certificate');
}
}
class Certificate extends Eloquent
{
public function user()
{
return $this->belongsTo('User');
}
}
Controller
$users = User::with(array('certificates' => function($query)
{
$query->where('type','!=','SL');
}))->get();
return View::make('yourView')->with('users',$users);
View
#foreach($users as $user)
{{ $user->name }} // User's name
#foreach($user->certificates as $certificate)
{{ $certificate->certificate_type }} // Certificate type shouldn't be 'SL'
#endforeach
#endforeach
I hope this helps!