Accessing more remote model in Laravel 5.4 -updated - laravel

I have three models
Photo
-id
-path_full
Person
-id
-name
Face
-id
-person_id //was people_id
-photo_id
I am trying to access the person name from the Photo model.
Face Model:
public function photo()
{
return $this->belongsTo(Photo::class);
}
public function person()
{
return $this->belongsTo(Person::class);
}
Photo Model:
public function faces()
{
return $this->hasMany(Face::class);
}
Person Model:
public function faces()
{
return $this->hasMany(Face::class);
}
In my controller I load the Photos like this:
$photos = Photo::with('faces')->paginate();
In my blade template I want to access the name of the face in the photo.
I got this far.
This is in a foreach hence singular $photo:
{{implode($photo->faces->pluck('people_id')->toArray(),', ')}}
How can I get the name of the person instead?
Solution
I needed this in my view and note my change to the db to person_id so eloquent could do it's magic.
//Controller:
$photos = Photo::with('faces.person')->paginate();
//View:
#foreach($photo->faces as $face)
{{$face->person['name']}}
#endforeach

You can eager load the person data all the time you call faces on the Photo's model:
// Photo.php
public function faces()
{
return $this->hasMany(Face::class)->with('person');
}
Or in your query, you can do this to eager load only at that time:
$photos = Photo::with('faces', 'faces.person')->paginate();
Now you can access like this:
$photos->first()->faces->first()->person->name; // will print the name of the person of the first face of the first photo...
I hope this answer can be helpful.

Try to get the person of the face by changing your query like so:
Photo::with(['faces' => function($query) {$query->with('person')}])->paginate();
I am not quite sure about the syntax, but this is how you can nest relations in an eloquent model. A shorter way to write this might be: Photo::with('faces.person')->paginate();
Some more information is provided here.

Related

How to filter Eloquent collection with data from pivot table

I'm searching for a fine and neat solution to filter my Eloquent collection with the data in the related pivot table. I actually found a solution to my problem already though it feels like my solution is somehow bad. Here is, what I got:
Two models Video and User where every user can track separately the progress on a video. For this I need to save the progress for every video in the ratings table related to the user. When you want to search for just the progress you can do that besides some other filters (category, full text search). What I find a bit quirky is that I have to double "where()" the part where I'm checking the video progress for the logged in user.
Video.php
class Video extends Model
{
use SearchableTrait;
use Taggable;
[...]
public function videocreator(){
return $this->belongsTo('App\User', 'create_user_id');
}
public function users(){
return $this->belongsToMany('App\User', 'progress', 'video_id', 'user_id')
->withPivot('progress_index')
->withTimestamps();
}
[...]
}
User.php
class User extends Authenticatable
{
use Notifiable;
[...]
public function videoscreated(){
return $this->hasMany('App\Video');
}
public function videos(){
return $this->belongsToMany('App\Video', 'progress', 'user_id', 'video_id')
->withPivot('progress_index')
->withTimestamps();;
}
}
VideoController.php
class VideoController extends Controller
{
public function index(Request $request)
{
[...]
$videos = Video::with('videocreator')->with(['users' => function ($query) {
$query->where('users.id', '=', auth()->user()->id);
}])->latest();
if($request->filled('progress') && $request['progress'] !== 'all'){
$videos = $videos->whereHas('users', function($query) use($selectedProgress) {
$query->where('progress_index', $selectedProgress)->where('users.id', '=', auth()->user()->id);
});
}
$videos = $videos->get();
[...]
}
As you can see this part where('users.id', '=', auth()->user()->id) is duplicated. A friend of mine from the Ruby on Rails Faction proposed to come from the User Model first and then fetch the Videos (that's how you would do it there). Though this way you would limit the videos to the user from the progress table. This is not what you want. The App shall track only the per user progress per video, meaning, all the users can see all the videos (just not the progress which is not their own.
Another approach would be to put the part where you filter for the user into the relationship. A method like: myProgress() or something similar.
What is your opinion to that? Is there a more 'eloquent' way to solve that?
Thanks in advance for reading this post!
I would do something like this :
// Get an instance of the videos relationship of the current authenticated user
// Eager load the videocreator relationship
$videoQuery = request()->user()->videos()->with('videocreator');
if ($request->filled('progress') && $request->input('progress') !== "all") {
// Constrain the query : only get the videos with rated_index equal to $selectedProgress
$videoQuery = $videoQuery->wherePivot('rated_index', $selectedProgress);
}
// Finally, run the query against the database
$videos = $videoQuery->latest()->get();

Add specific related field to response

I have a controller with the action:
public function getCities(): JsonResponse
{
return response()->json([City::all()], 200);
}
Entity City has relation to Country.
How I can add country.id for every item in the result City::all()?
Laravel has amazing feature for creating virtual attributes. Add these lines to your City model for this:
NOTIFICATION: I assume you have the CountryCity model
public $appends = ['country_id'];
public function getCountryIdAttribute()
{
$country = CountryCity::where('city_id',$this->id)
if($country){
return $country->id;
}
return null;
}
You should look at the documentation : https://laravel.com/docs/8.x/eloquent-relationships
The best way is to use eager loading, using :
City::with('country')->get();
Then, you can access country id like :
$city->country->id;

How to retrieve multiple relations with multiple tables in laravel eloquent

I'm using Laravel 5.8 to build a babysitting site. I have 4 tables with different relationships as below:
please see this image
The relationships are:
Babysitter->hasMany(session)
Sessions->hasOne(Review)
Sessions->hasOne(Kids)
Sessions->hasOne(Babysitter)
Sessions->hasOne(Parent)
I want to achieve 2 things:
First one
I want to show this result when listing all babysitters. I'm showing this information for each babysitter:
plsease see this image
See here what I couldn't achieve
plsease see this image
This is my code
Sitters::where('Status', 'active')->where('Verified', 1)->get();
Second one
Also, I've tried to show kids name with parent review as shown here:
plsease see this image
This is what i'm using
Sessions::select('Reviews.*', 'Sessions.Parent_id')->join('Reviews', 'Reviews.Session_id', '=', 'Sessions.id')->with('owner')->where('Trainer_id', session('user')->Id)->where('Status', '=', 'complete')->with('owner')->orderBy('Sessions.id', 'DESC')->get();
Here is Session.php Model
public function owner(){
return $this->belongsTo('App\Models\Parents', 'Parent_id');
}
As discussed change the relations:
Babysitter->hasMany(sesstion)
Sessions->hasOne(Review)
Sessions->belongsTo(Kids)
Sessions->belongsTo(Babysitter)
Sessions->belongsTo(Parent)
First one
in Babysitter.php declare the following attributes
class Babysitter extends Model
{
public function reviews()
{
$this->hasManyThrough(Review::class, Session::class);
}
public function getAverageReviewAttribute()
{
return $this->reviews()->avg('Rating');
}
}
Then you just need to call it on the model instance.
$babysitter = Babysitter::first();
return $babysitter->average_review;
Second one
Just use the relation
$babysitter = BabySitter::with(['sessions' => public function ($session) {
$session->with(['review','parent','kids']);
})->where('trainer_id', '=', session('user')->Id) //did not understand this condition
->first();
This assumes you have parent, kids and review relation declared on Session::class. (change the names if needed)
After a few days of searching & testing, this is what worked for me:
Inside (Sitters) Model, put this relation
public function sessions()
{
return $this->hasMany(Sessions::class, 'sitter_id')
->withCount('reviews')
->withCount(['reviews as review_avg' => function($query){
$query->select(DB::raw('AVG(Rating)'));
}]);
}
Also, inside (Sessions) Model, put this relation
public function reviews()
{
return $this->hasOne(Reviews::class, 'Session_id');
}
Now you query like this
return $sitters = Sitters::with('sessions')->get();
I hope this can help someone :)

Laravel 5.7 query with 3 tables

I'm pretty new to Laravel and i got stuck with building a more complex query:
I've 3 tables with their models:
Codes:
id
description
factor
public function purposes() {
return $this->hasMany('App\Purpose', 'code_purposes', 'code_id', 'purpose_id');
//I could be wrong here
}
Purpose:
id
name
description
Code_purposes:
code_id
purpose_id
public function codes() {
$this->belongsToMany('App\Code'); //I could be wrong here
}
public function purposes() {
$this->belongsToMany('App\Purpose'); //I could be wrong here
}
What I want is to fetch all the codes with the condition where the purposes name = 'some_name'
I thought this would be easy with the relationships, but I can't figure it out.
So how do I do this in Laravel?
In Code model:
public function purposes() {
return $this->belongsToMany('App\Purpose');
}
In Purpose model:
public function codes() {
return $this->belongsToMany('App\Code');
}
Now you can get data like:
$codes = Purpose::where('name', 'some_name')->first()->codes;
Relation table name must be code_purpose. And no need any model for this table.
Source: Laravel Many To Many Relationships

Laravel - Filter records by distinct relation

Right now I have the following models structure,
Country -> State -> City -> Student
And the Student model includes first_name and last_name.
So, I want countries to by filtered by giving student's first name. Like if I give John then I want list of countries which has students whose first name is John.
I was trying something like this,
I added a method called Students() in Country model and returning Student instances from that method. But now I stuck to find out how to filter countries.
Thanks in anticipation.
I came across a similar question recently and I came with the following solution myself. Probably, there are easier ways, but this will get you going for now, I guess.
First we query all the users with first_name == John, as you described, and we limit the query to output only the ID's.
$users = User::where('first_name', 'John')->get()->pluck('id');
We then cross-compare this with the result of Students() from the country model. As I don't know what you're querying for to get the country that you want, I'll just take the Netherlands as an example — that's where I'm from.
$users = Country::where('name', 'the Netherlands')->students()->whereIn('id', $users)->get();
For this to work, you must make sure that within the Students()-function in your model, the get() is left out.
Final 'result':
$users = User::where('first_name', 'John')->get()->pluck('id');
$users = Country::where('name', 'the Netherlands')->students()->whereIn('id', $users)->get();
in the Student Model create this:
public function city()
{
return $this->belongsTo(City::class,'city_id', 'id');
}
and in the City Model create this:
public function state()
{
return $this->belongsTo(State::class,'state_id', 'id');
}
Finally in the State Model:
public function country()
{
return $this->belongsTo(Country::class,'country_id', 'id');
}
For example if you made things like that in controller:
$students = Student::where('name', 'John')->get();
in the view:
#foreach($students as $student)
$student->city->state->country->country_name;
#endforeach
You can access like that.
If you have setup the model and relationship properly then you need to call them using in with function
$students = Student::where('name','John')->with('city.state.country')->get();
Loop through the $students
#foreach($students as $student)
{{ $student->city->state->country }}
#endif

Resources