Laravel 5 eager loading, select column from eloquent relationship - laravel

I am trying to
1. select `DOS`,`name`,`fin`,`ins_type` from events table.
2. site_name from sites table
3. client_name from clients table
but unable to access site_name, client_name column in select statement
->select('DOS','name','fin','ins_type')
How to add these columns site.site_name, client.client_name in above select statement.
Eloquent query is
$events = Event::with([
'site'=>function($q){
$q->select('site_id','site_name','client_id');
},
'site.client'=>function($q2){
$q2->select('client_id','client_name');
}])
->select('DOS','name','fin','ins_type')
->get();

How about this?
$events = Event::with([
'site'=>function($q){
$q->with(['client'=>function($qq){
$qq->select('client_id','client_name');
}])->select('site_id','site_name','client_id');
}])
->select('DOS','name','fin','ins_type')
->get();

I found a simple solution from the comment section in here. It is possible to put the column selection in the model. So in your case, your Event model could be something like this.
public function site()
{
return $this->belongsTo('App\Site')->select('id', 'name', 'client_id');
}
Then, in your controller you simply call the function.
$events = Event::with('site')->get();
I haven't tried how this goes with the nested eager loading though.

I myself got a little problem with these also until i discover that you need to add the foreign key to the select statement inside the "with" closure.
the column that relates "client" to "site" and "site" to "Event"

Related

Contain Laravel 8 rightJoin data in its own property

I'm successfully doing a rightJoin in Laravel 8 Eloquent. I'd like to isolate data from the table I am joining as its own property on the object.
My Eloquent code:
return Book
::rightJoin('tableB', 'tableB.book_id', '=', 'book.id')
->where('tableA.user_id', $uid)
->get();
Right now the data I am getting is formatted as e.g.:
[
tableA.col1,
tableA.col2,
tableB.col1,
tableB.col2
]
I'd like to have the returned objected structured like this instead:
[
tableA.col1,
tableA.col2,
tableB [
col1,
col2
]
]
I'm doing this to get a list of books that the user has started, and want the progress info as its own property to satisfy the structure expected on the front end.
I could of course process and restructure the data using PHP on the back end, but this feels rather ugly. Is there a Laravel 8 Eloquent feature that can help me structure the returned data in the way the front end right now expects?
What you are looking for is a belongsTo (or hasOne depending on the case) relationship.
Models/Book.php
public function tableB() {
return $this->belongsTo(TableB::class);
}
Then when on the eloquent builder you can use with to retrieve the books and the TableB records associated with each book
$books = Book::whereHas('tableB', function($query) {
$query->where('id', ...); // this is a query that will put a constraint of which results are returned. Adjust according to your necessity
})->get();

Eloquent with diferent relationships for same table

i need some hints and what is the best way and pratice with laravel to solve this problem!
I have a main table called colors with the fields category_id and type and i have in my models 2 relationships for the category_id, the bluecategory and redcategory.
In some cases i use the id from bluecategory and in other exemples i use the redcategory id.
Now in one page i want to shows all the results from colors, but i can't do this $data->bluecategory or $data->redcategory because i dont'no what record is using what relationship.
My ideia was using a function to send parameters category_id and type and inside the function discover what is the correspondente relashion and return the correct result.
But how i can handle this?
Sorry its a bit confuse!
You could add a scope to your modal and than chain it to your existing query.
Example
public function scopeName($query, $catID, $type)
{
return $query->where('category_id, $catID)
->where('type', $type);
}

Wrong ID returned from local Eloquent scope

In my Laravel project I've got a model (and an underlying table) for lessons. Now I'm trying to write a local scope for returning all lessons that have been finished by a particular user. The definition of "finished" is that there exists a row in a table named "lesson_results" with this lesson's ID and the users ID.
My scope currently looks like this:
public function scopeFinished($query, User $user)
{
return $query->join('lesson_results', function($join) use($user)
{
$join->on('lesson_results.lesson_id', '=', 'lessons.id')
->where("user_id", $user->id);
});
}
This kinda works. When I do a Lesson::finished($user)->get() I get out the correct lessons for that user. However the lessons in the collection returned all have the wrong ID's! The ID's I see are the ID's from the lesson_results table. So when I check $lesson->id from one of the returned items I don't get the ID from that lesson, but the ID from the corresponding row in the lesson_results table.
I've checked in mysql and the full query sent from Laravel is the following
select * from `lessons` inner join `lesson_results` on `lesson_results`.`lesson_id` = `lessons`.`id` and `user_id` = 53
This query DO return two columns named id (the one from the lessons table and the one from the lesson_results table) and it seems Laravel is using the wrong one for the result returned.
I don't know if I'm going about this the wrong way or if it's a bug somewhere?
This is on Laravel 7.6.1.
edit: Ok, I think I actually solved it now. Not really sure though if it's a real solution or just a workaround. I added a select() call so the return row now is
return $query->select('lessons.*')->join('lesson_results', function($join) use($user)
...which makes it only return the stuff from the lessons table. But should that really be needed?
One of the same column names will be covered by the other.
Solution 1:
Specify the table with the column, and alias the other table's column if it has same column name.
Lesson::finished($user)->select('lessons.*', 'lesson_results.id AS lesson_result_id', 'lesson_results.column1', 'lesson_results.column2',...)->get();
Solution 2:
Or you can use Eloquent-Builder eager-loading whereHas,(Assuming you have build the relationship between model Lesson and model LessonResult)
public function scopeFinished($query, User $user)
{
return $query->whereHas('lessonResults', function($query) use($user)
{
$query->where("user_id", $user->id);
});
}
So you can get lesson like this:
Lesson::finished($user)->get();

Retrieve custom fields from child of a child model in Laravel eloquent

I'm trying to retrieve custom fields from a model using the with() statement in Laravel. When I just get all fields the child object comes ok, but when I try to retrieve just some fields of this child abject it returns to me the following error:
Integrity constraint violation: 1052 Column 'id' in field list is ambiguous
below I'll show how I'm doing these queries.
When I execute the following query it works:
$cfps = CallForPaper::select('id', 'speech_id')->with([
'speech:id,title,description',
'speech.speakers'
])->get();
But when I execute the code below it returns the error which I have mentioned above.
$cfps = CallForPaper::select('id', 'speech_id')->with([
'speech:id,title,description',
'speech.speakers:id,name,email'
])->get();
Note: than only modification in this code is the line: 'speech.speakers:id,name,email'
in my model Speech, the speakers relationship is the following:
public function speakers()
{
return $this->belongsToMany(Speaker::class, 'speech_speaker');
}
You will need to define the table_name for the id's your are selecting:
$cfps = CallForPaper::select('id', 'speech_id')->with([
'speech:TABLE_NAME.id,title,description',
'speech.speakers:TABLE_NAME.id,name,email'
])->get();
you can also do the following, ie. rename the id:
$cfps = CallForPaper::select('id', 'speech_id')->with([
'speech:TABLE_NAME.id as speechID,title,description',
'speech.speakers:TABLE_NAME.id as speakerID,name,email'
])->get();

Query builder - pivot table laravel

How do I get only users from database where they have no role assigned?
User belongs to many roles, the exact relation is:
public function roles() {
return $this->belongsToMany(Role::class, 'user_has_roles', 'user_id', 'role_id');
}
My problem is, that I do only work with Query builder, not with the model so I do not want to use relation.
EDIT:
I am working with migrations, so please no solutions when using model.
You can use doesntHave, I believe this uses QueryBuilder only.
User::doesntHave('roles')->get();
Reference: https://laravel.com/docs/5.6/eloquent-relationships
UPDATE
Using raw SQL query, you can do this.
Basically what this tries to do is to get all users where its id is not found in the user_has_roles table (meaning it doesn't have any relations)
SELECT *
FROM users
WHERE id NOT IN (SELECT user_id FROM user_has_roles)
I think you can convert this into a JOIN or something. I'm not that expert in raw SQL though.
UPDATE Trying Query Builder
$usersWithoutRoles = DB::table('users')
->whereNotIn(function ($query) {
$query->select(DB::raw('user_id'))
->from('user_has_roles')
->whereRaw('user_has_roles.user_id = users.id');
})
->get();
Reference: SQL - find records from one table which don't exist in another
You can try other alternatives there.

Resources