Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between method calls $model->relation(); and $model->relation;

There is some basic understanding/theory here that I am missing.I don't understand the difference between these function calls:

$distributors = $store->distributors(); $distributors = $store->distributors; $distributors = $store->distributors()->get(); $distributors = $store->distributors->get(); 

What I am trying to accomplis here is to get a list of the distributors for a store (a many to many relationship), and they get each distributors list of beers into one giant list.

foreach ($distributors as $distributor)  {     $available_beers = array_merge($distributor->beers(), $available_beers); } 

I don't know if that is the best way to do this and I can't get it to work. Similar to the first list of methods, I don't know if I need ->$beers or ->$beers()

Update

Thanks to everyone who answered! This will be a good reference for me going forward. My biggest lesson was the difference between getting a collection back, vs getting the query builder/relationship object back. For future reference to those who find this question, here is what I set up in my controller:

$store = $this->store->find($id)->first(); $distributors = $store->distributors; $beers = []; foreach ($distributors as $distributor){     $beers = array_merge($distributor->beers->lists('name', 'id'), $beers); } 
like image 836
hodale Avatar asked Jan 29 '15 19:01

hodale


People also ask

How many types of relationships are there in Laravel?

One To One (Polymorphic) One To Many (Polymorphic) Many To Many (Polymorphic)

What is with () in Laravel?

with() function is used to eager load in Laravel. Unless of using 2 or more separate queries to fetch data from the database , we can use it with() method after the first command. It provides a better user experience as we do not have to wait for a longer period of time in fetching data from the database.

What is belongsTo in Laravel?

A belongsTo() relationship matches a related table's 'id' to a 'localKey' field on 'this' model. Another way to think about it is the belongsTo() relationship should live on the model that has the field that links to the related tables id.

What is polymorphic relationship in Laravel?

A one-to-one polymorphic relationship is a situation where one model can belong to more than one type of model but on only one association. A typical example of this is featured images on a post and an avatar for a user. The only thing that changes however is how we get the associated model by using morphOne instead.


2 Answers

Short answer

$model->relation() returns the relationship object

$model->relation returns the result of the relationship


Long answer

$model->relation() can be explained pretty simple. You're calling the actual function you defined your relation with. Yours for distributor probably looks somewhat like this:

public function distributors(){     return $this->hasMany('Distributor'); } 

So when calling $store->distributors() you just get the return value of $this->hasMany('Distributor') which is an instance of Illuminate\Database\Eloquent\Relations\HasMany

When do you use it?

You usually would call the relationship function if you want to further specify the query before you run it. For example add a where statement:

$distributors = $store->distributors()->where('priority', '>', 4)->get(); 

Of course you can also just do this: $store->distributors()->get() but that has the same result as $store->distributors.


Which brings me to the explanation of the dynamic relationship property.

Laravel does some things under the hood to allow you to directly access the results of a relationship as property. Like: $model->relation.

Here's what happens in Illuminate\Database\Eloquent\Model

1) The properties don't actually exist. So if you access $store->distributors the call will be proxied to __get()

2) This method then calls getAttribute with the property name getAttribute('distributors')

public function __get($key) {     return $this->getAttribute($key); } 

3) In getAttribute it checks if the relationship is already loaded (exists in relations). If not and if a relationship method exists it will load the relation (getRelationshipFromMethod)

public function getAttribute($key) {     // code omitted for brevity      if (array_key_exists($key, $this->relations))     {         return $this->relations[$key];     }      $camelKey = camel_case($key);      if (method_exists($this, $camelKey))     {         return $this->getRelationshipFromMethod($key, $camelKey);     } } 

4) In the end Laravel calls getResults() on the relation which then results in a get() on the query builder instance. (And that gives the same result as $model->relation()->get().

like image 161
lukasgeiter Avatar answered Oct 27 '22 20:10

lukasgeiter


The direct answer to your question:

  • $store->distributors() will return the actual relationship object (\Illuminate\Database\Eloquent\Relations\BelongsToMany).
  • $store->distributors will be a collection containing the results of the relationship query (\Illuminate\Database\Eloquent\Collection).
  • $store->distributors()->get() will be a collection containing the results of the relationship query (\Illuminate\Database\Eloquent\Collection).
  • $store->distributors->get() should return an error since you're calling get() on a Collection object and the first parameter is not optional. If not an error, it should at least return null.

More information:

Given the following model:

class Store extends Eloquent {     public function distributors() {         return $this->belongsToMany('Distributor');     } } 

Calling the relationship method ($store->distributors()) will return to you the relationship (\Illuminate\Database\Eloquent\Relations\BelongsToMany) object. This is basically a query object which you can continue to modify, but you still need to call some type of method to get the results (e.g. get(), first(), etc).

However, accessing the relationship attribute ($store->distributors) will return to you a collection (\Illuminate\Database\Eloquent\Collection) object containing the results from executing the relationship query.

By default, the relationship attribute is created and assigned a value the first time it is accessed (known as "lazy loading"). So, the first time you access $store->distributors, behind the scenes it is executing the relationship query, storing the results in the $store->distributors attribute, and then returning those results. However, it only does this once. The next time you access $store->distributors, the attribute already contains the data, so that is what you are accessing.

To illustrate this:

// the following two statements will run the query twice $r1 = $store->distributors()->get(); $r2 = $store->distributors()->get();  // the following two statements will run the query once. // the first statement runs the query, populates $store->distributors, and assigns the variable // the second statement just accesses the data now stored in $store->distributors $r3 = $store->distributors; $r4 = $store->distributors;  // at the end, $r1 == $r2 == $r3 == $r4 

Relationships can also be "eager" loaded, using the with() method on the query. This is done to alleviate all of the extra queries that may be needed for lazy loading (known as the n+1 problem). You can read more about that here.

like image 43
patricus Avatar answered Oct 27 '22 20:10

patricus