How do I reload a relation collection in laravel? - php

In Laravel, after using attach() or detach() to add or remove something from a relation, the collection has not changed. So if I have a model whose relation contains [1, 2], after this:
$model->relation()->detach(1);
$model->relation()->attach(3);
it will still contain [1, 2]! How do I refresh it?

You can easily tell laravel to load a relation with a single command:
$model->load('relation');
Will tell it to refresh the relation collection, and $model->relation will now show the correct values.
Also unloading a relation will be like this:
$model->unsetRelation('relation')

either just unset it and let the system reload on demand.
unset($model->relation)
or
$model->unsetRelation('relation');
And let it be loaded on request.

From Laravel 7.x you can use $model->refresh() for refreshing the model and it's relations.
Here the docs

Conclusion: three solutions in here
$model->load('relation');
unset($model->relation);
$freshCollection = $user->roles()->get();`

It is possible to use Eloquent query builder:
$freshCollection = $user->roles()->get();

If you want to force all your relations to reload on an as-needed basis and you're inside your model, you can use:
$this->relations = [];

$model->fresh() did the job for me.
Wanted to replicate multiple levels of nested models then do a loop over them.
Laravel was caching the previous relation and not the new "current" relation.

Related

Laravel collection: pluck before serialization

I have a laravel Model with a one to many relationship which the user can edit via a multiple select tag.
Before exporting the model as a JSON, I use the "pluck" method to get an array of related IDs instead of an array of models, so that they can be used in the select tag and later be synced again with the "sync" method of Laravel.
However the result of "pluck()" seemingly doesn't persist over serialization. The following code doesn't work -upon serialization, "relationship" becomes again an array of objects-
$model->relationship = $model->relationship->pluck('id');
This one, however, does what it should: somePropertyIHaveJustCameUpWith is an array of IDs
$model->somePropertyIHaveJustCameUpWith = $model->relationship->pluck('id');
1) Why does this happen?
2) I have seen there is this resources way in the documentation, but creating an entire new class for something that could be solved with a single line of code feels like a bit overkill. Isn't there a cleaner way to do that?
I think this is likely a result of the way the model implements toArray().
The you can trace the steps taken, but eventually the relations are read from the $this->relations property on the Model, not from each individual relationship.
So, instead of setting the value of your relation directly like:
$model->relationship = $newValue
... you could try setting it using:
$model->setRelation('relationship',$newValue)
This will update the $model->relations property.
This should allow the toArray() method to get the new value that you set when serializing.
Note that the toJson() method in turn calls the toArray() method when serializing. So either approach will be the same result.

Basic Eloquent Relationship Enquiry - One to One

**USER MODEL**
public function post(){
return $this->hasOne('App\Post','user_id','id');
}
**WEB ROUTE**
use App\User;
Route::get('/{id}/post',function($id){
return User::find($id)->post;
});
Hi everyone, I'm fairly new to both PHP and Laravel and have been struggling a bit. I just have 2 questions for this code.
In the web routes, why doesn't post have any () beside it? It was declared a function in the user model. And.. I am unsure of how these relationships work (correct me if I am wrong) but does the code above look for a user with a specific $id and connects it with a post having a similar $user_id value?
For the first bit, it is a dynamic property, here you can find how you can actually make one yourself Laravel: create a dynamic property. They essentially work because the result is a single object search based on the id, since it doesn't have to retrieve a collection it allows itself to be accessed like an attribute of the object.
And yea pretty much on the second one. It also uses laravels models to retrieve the data from the database so that you get an object back without needing to create the repositories yourself.
There are major differences between User::find($id)->post and User::find($id)->post(). First one is returning the result of the related relations so you get the post that has user_id equal to $id.
The second one returns a query builder,so you can add more conditions. For example User::find($id)->post()->where("status", 1)->get().

Laravel 5 Model chaining

Assume A,B,C,D are models. Is there a way to cleanly chain models like so: a->b()->c()->d()->get();? When trying to do this, I get an error since a->b(), b->c(), and c->d() all return sets and not a single object.
Some people have suggested eager loading in other sites, but I have no idea how to use them. So far I have tried using a->load('b.c.d'); in hopes of loading all the models to 'a' but it didnt work.
How can I load all relevant models b,c,and d to a?
You can use with
$v = a->with('b.c.d')->get();
then you can use in your code like this
$v->b->c->d

Eloquent/Laravel: is there a counterpart to object->load('relation'), eg. object->unload('relation')?

When i update a relation, e.g. update parent_id on Child (Child belongsTo Parent, Parent hasMany Child) and respond with the Child->Parent object, somehow the returned Parent is still the old one. I think this is because the Parent is already loaded at that time.
I now want to unlaod the relations so the new Parent is fetched from db again.
Is there a way to unload the loaded relationships? Like you can lazy-load by using model->load('relation'), can you also unload it again?
Thanks a lot!
Unloading relations can be done by passing an empty array to the model
$child->setRelations([]);
when you call a relation on the model after that, it will be reloaded at that moment.
.. in the current version (5.x) at least, maybe not at the time of your question :)
You can unload relations by unsetting the magic property (at least in Laravel 5.3 and up).
Usage:
unset($model->relation);
What makes this work (from the Model class):
public function __unset($key)
{
unset($this->attributes[$key], $this->relations[$key]);
}
It does the same as $model->setRelations([]), but for a specific relation (instead of unloading all relations).
As of Laravel 5.6, there is an unsetRelation(string) function.
$parent->load('child');
$parent->unsetRelation('child');
I think this provides a little more readability if you're just trying to unset a single relationship, rather than removing all by $parent->setRelationships([]).
There is no counterpart to load that would allow to unload a relationship.
However, to reload a relation from the database you can just call load again
$parent = $child->load('relation');
// change parent_id
$parent = $child->load('relation');
I've quickly looked at the source of laravel and found no hint of some kind of caching. So it just performs a new query fetching the relationship again.
Thanks #Jarek Tkaczyk for confirming my assumption

Laravel - Check & Make eager load of relational model.

I'am trying to check if the record has relation and at the same time I want to make an eager load so:
User::has('item')->with('item')->get();
I've noticed that with method do not check if the record has a relation, is this the right sequence or there are a shorter way to code this?
Just try this:
// Pull all blog users that have
// at least one related model/item
$users = User::has('item')->get();
Instead of this:
User::has('item')->with('item')->get();
Reference on Laravel website.

Categories