Laravel get Eloquent relation by same name as its attribute - php

I have database tables like this:
shoot: id, name, programme
programme: id, name
The eloquent relationship in the shoot is defined like this:
public function programme() {
return $this->belongsTo('App\Programme', 'programme', 'id');
}
When using dd(), I can see this is working correctly:
dd(Shoot:where('id','=',1)->with('programme')->first());
// prints the object with programme listed under the relationship
However when I eager-load the shoot and attempt to get the programme object, I retrieve the shoot attribute "programme" instead. E.g.:
$shoot = Shoot:where('id','=',1)->with('programme')->first();
echo $shoot->programme; // returns 1, not App\Programme object.
Is there a solution to this without having to rewrite masses of the codebase?

You shouldn't use the same name for the both relationship and column name, else you'll receive always the column name so try to edit one of them, I think the easiest one here is the relationship name :
public function programmeObj() {
return $this->belongsTo('App\Programme', 'programme', 'id');
}
Then call it as :
echo $shoot->programmeObj;
NOTE : But if you want to follow conventions you should replace the name attribute by programme_id so :
public function programme() {
return $this->belongsTo('App\Programme', 'programme_id', 'id');
}
Hope this helps.

To achieve what you after you will need to do the following:
$shoot = Shoot:where('id','=',1)->with('programme')->first();
$variable = $shoot->programme; // returns 1
$obj = $page->getRelationValue('programme') // returns App\Programme object.

This will returns always the column in your database if it exists, that's ID 1.
When you call dump($shoot); you should get the array with all attributes. But when you run the following you should get the name:
Your model:
public function programmeData() {
return $this->belongsTo('App\Programme', 'programme', 'id');
}
And your controller:
$shoot = Shoot:where('id','=',1)->first();
return $shoot->programmeData->name; // returns name
Hope this works!

Related

How to get only one note from relation belongsToMany?

I just have a table that has relation belongsToMany, BUT it was a mistake by developer so I can not change this structure SO I need to get only first(). However, when I take only first it return empty array but I need in object
$animals = Cat::query()->with(['types' => function($query) {
$query->first(); //wrong
}])
So how I can get only first? Because I need to order by this field and I can't because it is array
you can do this in two ways:
1- using hasOne relation:
class Cat {
public function firstType() {
return $this->hasOne(Type::class, 'type_id', 'id')->latest();
}
}
2- using staudenmeir/eloquent-eager-limit
after installing it you can write:
class Cat extends Model
{
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
public function firstType() {
return $this->hasMany(Type::class, 'type_id', 'id')->latest()->limit(1);
}
}
class Type extends Model
{
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
// ......
}
the advantage of HasEagerLimit trait is that you can limit the result not only to one but any number you want ...
now you can write:
$animals = Cat::query()->with('firstType');
You can add a attribute getter and set up into appends attribute. follow bellow example:
class Cat {
protected $appends = ['type'];
public function getTypeAttribute() {
// return the first element from your array of the belongsToMany relationship if it exists
return isset($this->types[0])? $this->types[0] : null;
}
}
That's important to remember this method will bring just one type. If you want to get the same type everytime, you create a diferente table where the cat table has the type_id column.
obs: Sorry for my english, it's still in working progress.

get field value from eloquent relationship

I have this relationship on eloquent
public function Manufacturer() {
return $this->hasOne('App\Models\ManufacturerModel', 'id')->select('name');
}
And this returns correctly the manufacturer name:
{"id":1,"serialnumber":"123_1","buydate":"2018-01-26 00:00:00","offservice":null,"deleted":"0","manufacturer":{"name":"HP"}}
I want to retrieve the name not as JSON object but as a string
{"id":1,"serialnumber":"123_1","buydate":"2018-01-26 00:00:00","offservice":null,"deleted":"0","manufacturer":"HP"}
The best way to define the relationship is:
public function Manufacturer() {
return $this->hasOne('App\Models\ManufacturerModel', 'id');
}
Then you can get the manufacturer name this way:
$your_object->manufacturer->name;
Or adding a wrapper method:
public function ManufacturerName() {
return $this->manufacturer->name;
}
Notice that when you refer to the relationship without parenthesis the query is executed and what you are accessing is the result. If you don't want the entire record to be queried you can do this:
public function ManufacturerName() {
return $this->manufacturer()->select('name')->get()->name;
}
By accessing the relationship with parenthesis you are getting the relationship definition and you can modify it before executing the query.
Not directly but you can achieve the functionality using a little bit trick of php, for example, if you would like to use it in string context as given below:
// {{ $someModel->manufacturer }}
echo $someModel->manufacturer; // or echo Manufacturer::find(1);
Then you can do it using the __toString magic method in Manufacturer model as given below:
public function __toString()
{
return $this->name;
}
In this case, even on json_encode($manufacturer) will give you just name so why don't you just use $model->manufacturer->name;

Eloquent hasMany relationship - can I retrieve an item by a seperate key's value?

I have set up a hasMany relationship between a model called Supplier, and a model called SupplierMeta.
SupplierMeta is linked to a table with the structure:
id, supplier_id, name, value
and the Supplier model has the following relationship defined:
public function meta() {
return $this->hasMany('Model\SupplierMeta');
}
The relationship itself works fine, but what I would like to do is define another function that searches within that relationship by the "name" field and returns the value. I would like it to work in this format:
$supplier->meta->field_name, or $supplier->meta()->field_name
This would either return the "value" field of the relevant SupplierMeta object, if it exists, or otherwise return false. Is this possible?
If you want to find suppliers by meta name, use whereHas() method:
Supplier::whereHas('meta', function ($q) use($metaName) {
$q->where('name', $metaName);
})->get();
Here's an example macro you can use, which enables you to do $supplier->meta->meta('field_name');
Collection::macro('meta', function ($name) {
return $this->first(function ($item) use ($name) {
return $item->name == $name;
});
});
You could then add a getMeta() method to your Supplier model:
public function getMeta($name) {
return $this->meta->meta($name);
}
This would let you do $supplier->getMeta('field_name');
I'm not totally sure how to get the last part of using it as a property like $supplier->meta->some_field however, this is a bit too magical for me.
I think you want to implement some __get() method, or a getMetaAttribute() on your model. Not totally sure on that part.

Select a specific column from laravel eloquent model

How do I select the column Field from the FormFactor model? I want to select only one column from the recordset.
public function ApiFormFactorDetails()
{
return $this->belongsTo('App\Model\FormFactor','formfactor_id')->select('name');
}
Eloquent models share pretty much the same syntax as the query builder. From the query builder documentation:
If you don't even need an entire row, you may extract a single value from a record using the value method. This method will return the value of the column directly:
$email = DB::table('users')->where('name', 'John')->value('email');
In your case:
public function ApiFormFactorDetails()
{
return $this->belongsTo('App\Model\FormFactor','formfactor_id')->value('name');
}
Actually, the way you are doing should work fine. You can keep the code in the function:
public function ApiFormFactorDetails()
{
return $this->belongsTo('App\Model\FormFactor','formfactor_id')->select('name');
}
And then, call it like this:
$object->ApiFormFactorDetails;
The return will have only name as attribute.

Laravel belongsTo only returns the id and not the entire object

I was under the impression that when I use a belongsTo relationship in my model, it will return the object but I seem to only get the id. Is this supposed to happen and what is the benefit of this?
This is my code:
From my Photo model
public function album()
{
return $this->belongsTo('Album', 'album');
}
And my PhotosController
$photo = Photo::find($id);
$album = $photo->album;
return 'albums/' . $album->folder . '/thumbs/' . $photo->file;
Don't mind the return, it's just for testing. I get an error:
Trying to get property of non-object
And a var_dump() shows that all I get is a string with the album's id
You get the id because your foreign key field has the same name as your relation.
So when you call $photo->album you are not touching the relation method (as dynamic property), but returning album field value, which happens to be the id.
The best is to rename the album field to album_id.
public function album()
{
return $this->belongsTo('Album', 'album_id');
}
Or since it follows the standard naming you can avoid specifying the album_id
public function album()
{
return $this->belongsTo('App\Album');
}
Try:
return $this->belongsTo('Album', 'album', 'id');
where 'id' is the name of the associated column on the album table
I had the same problem. I'm still not able to explain what happens. If you dump photo, I guess you would get both the album's id, and the album object in the relationships.
Threrefore, I somehow got it like this:
$album = $photo->album()->first();

Categories