Add specific related field to response - laravel

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;

Related

Get the ID of the first parent

I have three models that are related: Article, ArticleBlock, ArticleBlockImage
ArticleBlock is related with Article and ArticleBlockImage is related with ArticleBlock
Here are the relations
Article
public function article_blocks()
{
return $this->hasMany('App\Models\ArticleBlock');
}
ArticleBlock
public function article()
{
return $this->belongsTo('App\Models\Article');
}
public function article_block_images()
{
return $this->hasMany('App\Models\ArticleBlockImage');
}
ArticleBlockImage
public function article_block()
{
return $this->belongsTo('App\Models\ArticleBlock');
}
Further in the ArticleBlockImage model I have one function with which I need to get the ID of the current Article in the form of type $article->id
I am trying to do something like this, but I get the error
$article = article_block_images()->article_block()->article()->get();
"message": "Call to undefined function App\Models\article_block_images()",
$article = articleBlockImages()->article_block()->article()->get();
when you load your relation this way you load the relation type class, not the database records, relation classes like (HasMany, HasOne ...)
to get article_id you can a function like this:
public function article_id()
{
return $this->article_block->article_id; // without brackets
}
You wrote that ArticleBlock is related to Article and to ArticleBlockImage. Then you have the related Article ID inside the ArticleBlock.
That means if you have the ArticleBlockImage $articleBlockImage then you can write:
$articleId = $articleBlockImage->article_block()->article_id;

How can add additional Attribute in my JSON response with dynamic value in laravel

In my app i have a Posts and a Reacts table both are connected with relationship.
In App user can react to a post(like or dislike) and for retrieve this i'm using this function :
public function feed()
{
$posts=Post::with('user')
->with('reacts')
->withCount('comments')
->orderBy('created_at', 'DESC')
->get();
return response()->json(["posts" => $posts]);
}
the response is:
i want to add one more field in Posts Object for isUserLiked and if the current authenticated user liked the post then value will be true or false for him something like this:
i can add a additional field but how can i set the value dynamically for that
this is what i am doing in my Post Model:
protected $appends = ['isUserLiked'];
public function getIsUserLikedAttribute($id)
{
$react=React::where('user_id',auth()->user()->id)->where('post_id',$id)->exists();
return $react;
}
this is returning false because i don't know any way to pass the arguments(Post id).
is there any better way i can get the desired response? Thanks!
public function getIsUserLikedAttribute($id)
{
return React::where('user_id',auth()->user()->id)->where('post_id',$this->id)->exists();
}
In your user model:
public function reacts(){
return $this->hasMany(React::class);
}
public function scopeReactOnPost($query, $post_id){
return $this->reacts()->where(function($query) use ($post_id){
$query->where('post_id',$post_id);
});
}
and in your controller:
$user->reactOnPost($post_id)->first();
or
$user->reactOnPost($post_id)->get()->count();
Will let you know if user had any reaction on the specified post.
and for adding this to your json output you can artisan make a resource for your post model. Laravel Resources

HasMany Relation through BelongsToMany Relation

Is it possible to make Laravel relation through belongsToMany relations?
I have 4 tables:
1)Restaurants (id , name) - uses hasManyRelation with Workers table
2)Directors (id , name)
3)Directors_Restaurants (id, director_id, restaurant_id) - pivot table for connecting belongsToMany Restaurants with Directors
3)Workers (id, name, restaurant_id)
With this function in Directors model i can get all connected restaurants
public function restaurants()
{
return $this->belongsToMany('App\Restaurant','director_restaurant');
}
With this function in my code i can get all workers of all restaurants of one director
$director = Director::find(1);
$director->load('restaurants.workers');
$workers = $director->restaurants->pluck('workers')->collapse();
So my question is : can i declare similar relation in my Director model to get all its workers of all its restaurants?
Of course you can have hasMany relationship method on Director model with Eager Loading
just like below
public function restaurants()
{
return $this->hasMany(Restaurant::class)->with('restaurants.workers');
}
i can suggest a solution like this:
Director Model OPTION 1
public function getAllRestaurants(){
return $this->hasMany(Restaurant::class)->with('restaurants.workers');
}
Director Model OPTION 2
public function getAllRestaurants(){
$this->load('restaurants.workers');
return $this->restaurants->pluck('workers')->collapse();
}
You can get all restaurants anywhere
$all_restaurants = Director::find(1)->getAllRestaurants();
You can define a direct relationship by "skipping" the restaurants table:
class Director extends Model
{
public function workers()
{
return $this->belongsToMany(
Worker::class,
'director_restaurant',
'director_id', 'restaurant_id', null, 'restaurant_id'
);
}
}
You can define an accessor method in your model to hide some of the logic
# App/Director.php
// You'll need this line if you want this attribute to appear when you call toArray() or toJson()
// If not, you can comment it
protected $appends = ['workers'];
public function getWorkersAttribute()
{
return $this->restaurants->pluck('workers')->collapse();
}
# Somewhere else
$director = Director::with('restaurants.workers')->find(1);
$workers = $director->workers;
But ultimately, you still have to load the nested relationship 'restaurants.workers' for it to work.
Given your table attributes you could also define a custom HasMany relationship that looks like this
# App/DirectorRestaurant.php
public function workers()
{
return $this->hasMany(Worker::class, 'restaurant_id', 'restaurant_id');
}
# Somewhere else
$director = Director::find(1);
$workers = DirectorRestaurant::where('director_id', $director->id)->get()->each(function($q) { $q->load('workers'); });
But I don't recommend it because it's not very readable.
Lastly, there's the staudenmeir/eloquent-has-many-deep package where you can define that sort of nested relationship.
https://github.com/staudenmeir/eloquent-has-many-deep

Laravel oneToMany accessor usage in eloquent and datatables

On my User model I have the following:
public function isOnline()
{
return $this->hasMany('App\Accounting', 'userid')->select('rtype')->latest('ts');
}
The accounting table has activity records and I'd like this to return the latest value for field 'rtype' for a userid when used.
In my controller I am doing the following:
$builder = App\User::query()
->select(...fields I want...)
->with('isOnline')
->ofType($realm);
return $datatables->eloquent($builder)
->addColumn('info', function ($user) {
return $user->isOnline;
}
})
However I don't get the value of 'rtype' for the users in the table and no errors.
It looks like you're not defining your relationship correctly. Your isOnline method creates a HasMany relation but runs the select method and then the latest method on it, which will end up returning a Builder object.
The correct approach is to only return the HasMany object from your method and it will be treated as a relation.
public function accounts()
{
return $this->hasMany('App\Accounting', 'userid');
}
Then if you want an isOnline helper method in your App\User class you can add one like this:
public function isOnline()
{
// This gives you a collection of \App\Accounting objects
$usersAccounts = $this->accounts;
// Do something with the user's accounts, e.g. grab the last "account"
$lastAccount = $usersAccounts->last();
if ($lastAccount) {
// If we found an account, return the rtype column
return $lastAccount->rtype;
}
// Return something else
return false;
}
Then in your controller you can eager load the relationship:
$users = User::with('accounts')->get(['field_one', 'field_two]);
Then you can do whatever you want with each App\User object, such as calling the isOnline method.
Edit
After some further digging, it seems to be the select on your relationship that is causing the problem. I did a similar thing in one of my own projects and found that no results were returned for my relation. Adding latest seemed to work alright though.
So you should remove the select part at very least in your relation definition. When you only want to retrieve certain fields when eager loading your relation you should be able to specify them when using with like this:
// Should bring back Accounting instances ONLY with rtype field present
User::with('accounts:rtype');
This is the case for Laravel 5.5 at least, I am not sure about previous versions. See here for more information, under the heading labelled Eager Loading Specific Columns
Thanks Jonathon
USER MODEL
public function accounting()
{
return $this->hasMany('App\Accounting', 'userid', 'userid');
}
public function isOnline()
{
$rtype = $this->accounting()
->latest('ts')
->limit(1)
->pluck('rtype')
->first();
if ($rtype == 'Alive') {
return true;
}
return false;
}
CONTROLLER
$builder = App\User::with('accounting:rtype')->ofType($filterRealm);
return $datatables->eloquent($builder)
->addColumn('info', function (App\User $user) {
/*
THIS HAS BEEN SUCCINCTLY TRIMMED TO BE AS RELEVANT AS POSSIBLE.
ARRAY IS USED AS OTHER VALUES ARE ADDED, JUST NOT SHOWN HERE
*/
$info[];
if ($user->isOnline()) {
$info[] = 'Online';
} else {
$info[] = 'Offline';
}
return implode(' ', $info);
})->make();

Accessing more remote model in Laravel 5.4 -updated

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.

Resources