Laravel "with" changes variable case to snake case - php

In my Laravel app, I have A model that defines a relationship like:
public function vitalCategories()
{
return $this->belongsToMany(
'App\Models\Diagonals\VitalLabelCategory',
'vitalLabelCategoryMap',
'vitalLabelId',
'vitalLabelCategoryId');
}
When I query a record like below, I expect the relation to be available with the variable name vitalCategories
$vitalLabel = VitalLabel::where('label', 'confirmation')->with(['subscribers','vitalCategories','vitals'])->first();
return json_encode($vitalLabel);
However, the above query produces the relation with the variable name 'vital_categories' like this:
How can I make laravel stop changing the case of my variable for the relation to snake case?
Just for grins, I also tried:
$vitalLabel = VitalLabel::where('label', 'confirmation')->with(['subscribers','vitalCategories','vitals'])->first();
$vitalLabel->load('vitalCategories');
$vitalLabel->vitalCategories = $vitalLabel->vitalCategories() ;
return json_encode($vitalLabel);
which failed to see the related models:
so then I tried:
$vitalLabel = VitalLabel::where('label', 'confirmation')->with(['subscribers','vitalCategories','vitals'])->first();
$vitalLabel->load('vitalCategories');
$vitalLabel->vitalCategories = $vitalLabel->vital_categories;
return json_encode($vitalLabel);
which also failed to see the related models:

Laravel automatically converts the names of relationships from camelCase to snake_case when the model is converted to an array (toArray()) or json (toJson()).
So, the attribute on the model is actually vitalCategories, but when you dump it out as json, it will print as vital_categories.
If you would like to turn this off, you can set the $snakeAttributes property on your model to false.
public static $snakeAttributes = false;

Related

Laravel Eloquent - Relationships and methods. When to use ->users() vs ->users [duplicate]

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);
}
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().
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.
When you work with relationships with Eloquent the property is a collection (Illuminate\Database\Eloquent\Collection) of your relation white the method is a start of a new query.
Say your model looks like this:
class User extends Eloquent {
public function roles()
{
return $this->belongsToMany('Role');
}
}
If you try to access $user->roles, Eloquent will run the query and fetch all roles related to that user thanks to magic methods and returns an instance of Illuminate\Database\Eloquent\Collection. That class has a method called get, that's why $user->roles->get() works for you.
If you try to access the method, $user->roles(), you will instead get a query builder object so you can fine tune your query.
$user->roles()->whereIn('role_id', [1, 3, 4])->get();
That would only return roles where role_id is 1, 3 or 4.
So, the property returns a complete query and it results (Illuminate\Database\Eloquent\Collection) while the method lets you customize your query.
$distributors = $store->distributors();
Result of a method (function)
$distributors = $store->distributors;
Value of property (variable)
$distributors = $store->distributors()->get();
Take the first one, where it's the result of a method, if the method returns an object, this is a method in that object that was returned.
$distributors = $store->distributors->get();
If the property is an object, then it's calling a method in that property that's an object.
Re ->$beers vs ->$beers() that's a dynamic name of a property/method depending on what you're for. Just make a really rough guess at what you're doing, in your class you're going to have
$this->beers = array('bud','miller','sam');
and in your code using the $store object, you're actually going to go something like
$drink_type = 'beers';
$drink_list = $store->$drink_type;
And that will return $this->beers from $store, the same as writing $store->beers;
Imagine that the store class looks like this:
<?php
class Store {
public $distributors;
function __construct($distributors = array()) {
$this->distributors = $distributors;
}
public function distributors() {
return $this->distributors;
}
}
So the difference is:
$store = new Store(array('some guy', 'some other guy'));
$guys = $store->distributors; # accesing the $distributors property
$more = $store->distributors(); # calling the distributors() method.
The main difference is:
$distributors = $store->distributors() return instance of the relationship object like Illuminate\Database\Eloquent\Relations\BelongsToMany. You can use other conditions such as where after call this.
$store->distributors return instance of the collection Illuminate/Database/Eloquent/Collection. Laravel call the magic method __get under the hood. It will return a result of query relationship.
Maybe this will be usefull.
Access to method:
$object->method();
Access to property:
$object->property;

Relationship data inside getAttribute

I have a one-to-many relationship defined in Laravel:
TheModules has many TheModulesActions and TheModulesActions has one TheModules.
It works well, however I would like to set a custom attribute inside TheModulesActions called url.
To achieve that I need the following code:
protected $appends = array('url');
public function getUrlAttribute() {
$nameURL = SharedMethods::slugify($this->title);
$url = "#/control/{$module}/{$nameURL}/{$this->id}";
return $url;
}
Till now everything works, however $module variable is empty, all I have is $this->moduleID which is linked to TheModules.
I need $this->TheModules()->title but it doesn't work inside my getUrlAttribute method.
Is there any way to access relationship data inside getAttribute?
EDIT
The Relationships in TheModulesActions class are:
public function TheModule() {
return $this->belongsTo('App\TheModules');
}
The Relationships in TheModules class are:
public function TheActions() {
return $this->hasMany('App\TheModulesActions', 'moduleID');
}
Tried $this->TheModules->title:
Gives me this error:
ErrorException in TheModulesActions.php line 15:
Trying to get property of non-object
You can it by dropping the parenthesis () on the relation name as:
$this->TheModules->title
When you use parenthesis with relation name then Laravel returns an instance of query builder.

PHP 5.5 : how to convert array with 1 element to property of parent

I have this:
$foo = Foo::getFooById(100);
public static function getFooById($id)
{
return Foo::where('id', $id)->with('locations')->firstOrFail();
}
This returns an Eloquent collection.
$foo gives now the following properties:
$foo->name
$foo->locations
$foo->locations is an array with always 1 element
Just I want:
$foo->location = $foo->locations[0];
unset($foo->locations);
This code works, but is very terrible, because it happens on different places. How can I do this in a clear way?
You haven't shown it, but I assume you've defined Foo as having a one-to-many relationship with locations.
If there is always just a single location for any given Foo, then you should define it with a one-to-one relationship rather than one-to-many, then you'll be able to say $foo->location to get the single location object rather than $foo->locations to get an array of them.
In other words, I guess you have code in your Foo model that looks like this:
public function locations()
{
return $this->hasMany('locations');
}
You need to replace it with something more like this:
public function location()
{
return $this->hasOne('locations');
}
Then change with('locations') in your query function to with('location').

How to solve Laravel select queries conflicting with the $appends property on models?

I have a situation where I need a specific attribute accessor appended to one of my models automatically:
class Mission extends Eloquent {
protected $appends = ['launch_date_time'];
public function getLaunchDateTimeAttribute() {
return ($this->attributes['launch_approximate'] == null) ? $this->attributes['launch_exact'] : $this->attributes['launch_approximate'];
}
}
As you can see, this launch_date_time property is dependent on two other fields of my model that are actually in my database.
However, I now want to perform a query where only a certain number of fields are returned, as this is going to be sent over AJAX multiple times and I would rather use as few resources as possible:
// AJAX GET
// missions/all
public function all() {
$allMissions = Mission::with('featuredImage')->get(['mission_id', 'name', 'featured_image']);
return Response::json($allMissions);
}
The issue here is that I no longer need the launch_date_time attribute, so I have excluded it, **in doing so, my AJAX request does not work successfully:
Undefined index: launch_approximate on line 78 of H:\myproj\app\models\Mission.php
This is clearly because my model is attempting to append launch_date_time, of which launch_approximate is a dependency of. If I include all the required dependencies, all of them any my attribute that I want to append appear:
$allMissions = Mission::with('featuredImage')->get(['mission_id', 'name', 'featured_image', 'launch_approximate', 'launch_exact', 'launch_date_time']);
This is undesirable. Is there a solution where I can keep both setups?
The reason it is not working is because you are not retrieving the required fields from the database in the get method on your query. That is why you can't access launch_exact and launch_approximate because they are not set in the instance of your model.
So to make it work like you want. You would have to check if launch_exact and launch_approximate are set before you access them.
public function getLaunchDateTimeAttribute() {
if(isset($this->attributes['launch_approximate']) && $this->attributes['launch_exact']) {
return ($this->attributes['launch_approximate'] == null) ? $this->attributes['launch_exact'] : $this->attributes['launch_approximate'];
}
return null;
}
You can also set a whitelist with the $visible property and a black list with $hidden inside your model to not show certain attributes when outputing to json or a array take a look at the docs: http://laravel.com/docs/5.1/eloquent-serialization#hiding-attributes-from-json

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);
}
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().
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.
When you work with relationships with Eloquent the property is a collection (Illuminate\Database\Eloquent\Collection) of your relation white the method is a start of a new query.
Say your model looks like this:
class User extends Eloquent {
public function roles()
{
return $this->belongsToMany('Role');
}
}
If you try to access $user->roles, Eloquent will run the query and fetch all roles related to that user thanks to magic methods and returns an instance of Illuminate\Database\Eloquent\Collection. That class has a method called get, that's why $user->roles->get() works for you.
If you try to access the method, $user->roles(), you will instead get a query builder object so you can fine tune your query.
$user->roles()->whereIn('role_id', [1, 3, 4])->get();
That would only return roles where role_id is 1, 3 or 4.
So, the property returns a complete query and it results (Illuminate\Database\Eloquent\Collection) while the method lets you customize your query.
$distributors = $store->distributors();
Result of a method (function)
$distributors = $store->distributors;
Value of property (variable)
$distributors = $store->distributors()->get();
Take the first one, where it's the result of a method, if the method returns an object, this is a method in that object that was returned.
$distributors = $store->distributors->get();
If the property is an object, then it's calling a method in that property that's an object.
Re ->$beers vs ->$beers() that's a dynamic name of a property/method depending on what you're for. Just make a really rough guess at what you're doing, in your class you're going to have
$this->beers = array('bud','miller','sam');
and in your code using the $store object, you're actually going to go something like
$drink_type = 'beers';
$drink_list = $store->$drink_type;
And that will return $this->beers from $store, the same as writing $store->beers;
Imagine that the store class looks like this:
<?php
class Store {
public $distributors;
function __construct($distributors = array()) {
$this->distributors = $distributors;
}
public function distributors() {
return $this->distributors;
}
}
So the difference is:
$store = new Store(array('some guy', 'some other guy'));
$guys = $store->distributors; # accesing the $distributors property
$more = $store->distributors(); # calling the distributors() method.
The main difference is:
$distributors = $store->distributors() return instance of the relationship object like Illuminate\Database\Eloquent\Relations\BelongsToMany. You can use other conditions such as where after call this.
$store->distributors return instance of the collection Illuminate/Database/Eloquent/Collection. Laravel call the magic method __get under the hood. It will return a result of query relationship.
Maybe this will be usefull.
Access to method:
$object->method();
Access to property:
$object->property;

Categories