I'm currently facing a weird issue with one of my Laravel Models.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Gallery extends Model
{
protected $guarded = ['id'];
protected $with = ['member','photos', 'cover'];
public function member()
{
return $this->hasOne(Member::class, 'id', 'member')->setEagerLoads([]);
}
public function cover()
{
return $this->hasOne(Photo::class, 'id', 'cover')->setEagerLoads([]);
}
public function photos()
{
return $this->morphMany('App\Models\Photo', 'photoable')->setEagerLoads([]);
}
}
If I dump all galleries, each gallery has a cover which is a Instance of App\Models\Photo
$galleries = Gallery::all();
dump($galleries);
This also works with $galleries->toJson() and $galleries->toArray()
However, if I loop over galleries, cover is only an integer.
$galleries = Gallery::all();
foreach($galleries as $gallery){
dump($gallery->cover); // Integer instead of App\Models\Photo
}
While this returns a App\Models\Member:
$galleries = Gallery::all();
foreach($galleries as $gallery){
dump($gallery->member); //Instance of App\Models\Member
}
Laravel: 6.6.2
PHP: 7.4
Your Gallery model attribute $cover has the same name as relation.
Your model use $cover attribute which have integer value (foreign key to related model).
You could rename column cover for example to cover_id.
Relationship name cannot be the same as the column name in the table. Rename one or the other, would recommend to rename the column to cover_id.
You passed the wrong place of param in a relationship.
pass it as below.
public function member()
{
return $this->hasOne(Member::class, 'member','id')->setEagerLoads([]);
}
public function cover()
{
return $this->hasOne(Photo::class,'cover','id')->setEagerLoads([]);
}
Here example of laravel doc.
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
Related
Example copied from official Laravel Docs:
For example, a Post model and Video model could share a polymorphic relation to a Tag model. Using a many-to-many polymorphic relation in this situation would allow your application to have a single table of unique tags that may be associated with posts or videos. First, let's examine the table structure required to build this relationship:
posts
id - integer
name - string
videos
id - integer
name - string
tags
id - integer
name - string
taggables
tag_id - integer
taggable_id - integer
taggable_type - string
From a tag object I wanted to get all the videos and posts, to which that subjected tag belongs (in case of morphOne an morphMany I can do that by morphTo() method)
Laravel says, I need to define both the videos and posts methods in Tag model in order to define an inverse but I want a relation like taggables which will return the respected parent (whether it's Post or Video)
Reference
I need a similar thing like imageable (but it is polymorphic one to one and I need this kind of thing in many to many)
You can just use MorphOne/MorphMany in your pivot model.
https://laravel.com/docs/8.x/eloquent-relationships#defining-custom-intermediate-table-models
class Video extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable')->using(Taggable::class);
}
public function taggables()
{
return $this->morphMany(Taggable::class, 'taggable');
}
}
class Post extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable')->using(Taggable::class);
}
public function taggables()
{
return $this->morphMany(Taggable::class, 'taggable');
}
}
class Tag extends Model
{
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable')->using(Taggable::class);
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable')->using(Taggable::class);
}
public function taggables()
{
return $this->hasMany(Taggable::class/*, 'tag_id'*/)
}
}
use Illuminate\Database\Eloquent\Relations\MorphPivot;
class Taggable extends MorphPivot
{
public $incrementing = false; // this is the default value. Change if you need to.
public $guarded = []; // this is the default value. Change if you need to.
protected $table = 'taggables';
public function taggable()
{
return $this->morphTo();
}
public function tag()
{
return $this->belongsTo(Tag::class/*, 'tag_id'*/);
}
}
I am trying to retrieve the inverse side of the one to many relationship where the method is camelCase. i.e
Owning Class: OneToMany
class Brand extends Model
{
public function products()
{
return $this->hasMany(Product::class);
}
}
Owned Class
class Product extends Model
{
public function MyBrand()
{
return $this->belongsTo(Brand::class);
}
}
retrieve the inverse related model like this:
$product = Product::find(1);
$brand = $product->my_brand;
dd($brand->name);
Error, Trying to get property 'name' of non-object
I also tried this:
$brand = $product->myBrand;
it did not work.
however, if i make my method like below it works:
public function brand()
{
return $this->belongsTo(Brand::class);
}
question is : how to make it work when the method is in CameCase ?
Try this,
public function my_brand()
{
return $this->belongsTo(Brand::class);
}
And call this relation as,
$product = Product::find(1);
$brand = $product->my_brand;
dd($brand->name);
You need to change, method name in brand(), because Eloquent will automatically determine the proper foreign key column in the Brands table.
https://laravel.com/docs/7.x/eloquent-relationships#one-to-many
If you want to keep it MyBrand() you need to specify foreign key:
public function MyBrand()
{
return $this->belongsTo(Brand::class,'product_id');
}
Specify it's foreign key in relationship
class Product extends Model
{
public function MyBrand()
{
return $this->belongsTo(Brand::class,'brand_id');
//brand_id is foreign key in your product table
}
}
$product = Product::find(1)->with('MyBrand'); //But it will be good to eager load it
$product->MyBrand->name; //It will definitely return name now
I have two models Product and Images. I changed the route key name on the product model to use the slug field and i'm now unable to load the hasMany relationship with the Image Model
Here is the Product Model
class Product extends Model
{
protected array $with = ['images'];
public function getKeyName()
{
return 'slug';
}
protected array $guarded = [];
public function images() : HasMany
{
return $this->hasMany(Image::class, 'product_id');
}
}
and the Image model
class Image extends Model
{
protected array $guarded = [];
public function image() : BelongsTo
{
return $this->belongsTo(Product::class);
}
}
so when I try
Product::first()->images
it just returns an empty collection
but without overriding the getKeyName() method, everything works fine
getKeyName() will get the primary key for the model. it supports to return id, after you change it to slug, it will return slug
And hasManyHere's the source code ;
The third parameter LocalKey will use getKeyName() when it's empty.
If you still want to use hasMany, you need to pass the third parameter like this:
public function images()
{
return $this->hasMany(Image::class, 'product_id', 'id');
}
This will convert the Eloquent query to database query, which will take the right local key products.id.
So I have a model called data_storage and another model entity_states
I have to fetch the record from data_storage with entity_states where entity_state has data_storage_id and state_id.
How can I use eloquent to achieve this ?.
Or Ill have to use Query builder and use innerJoin?
Update1
My Actual Query
$this->values['new_leads'] = $data_storages->with('actions','states','sla')->where('wf_id',$wfid)->get();
My data_storage modal
class data_storages extends Model
{
//
protected $fillable = ['layout_id','member_id','company_id','team_id','data','status','wf_id'];
function actions()
{
return $this->hasMany('App\Models\ActionDataMaps', 'data_id', 'id' );
}
function states()
{
return $this->hasOne('App\Models\workflow_states','id','status');
}
function sla()
{
//Here I have to get those row from entity_states model where , data_storage_id and state_id
}
}
Thanks
Here's the more reasonable way to do it:
class DataStorage extends Model {
public states() {
return $this->belongsToMany(State::class,"entity_states");
}
}
class State extends Model {
public storages() {
return $this->belongsToMany(DataStorage::class,"entity_states");
}
}
Then you can eager-load related models via e.g.:
$storage = DataStorage::with("states")->first();
$storage->states->first()->column_in_related_state;
Or via the state:
$state = State::with("storages")->first();
$state->storages->first()->column_in_related_storage;
If there are additional columns in the pivot table entity_states then you can refer to them in the relationship as e.g.:
public states() {
return $this->belongsToMany(State::class)->withPivot("pivot_column");
}
In your model data_storage you can define a property / method entity_states to get them:
class data_storage extends Model
{
public function entity_states()
{
return $this->hasMany('App\entity_states','data_storage_id')->where('state_id ','=',$this->table());
}
}
Then you can access them in an instance by
$entityStatesOfDataStorage = $yourDataStorageInstance->entity_states;
See this link:
https://laravel.com/docs/5.3/eloquent-relationships
for Query Builder you may use this:
DB::table('data_storage')
->join('entity_states','data_storage.data_storage_id','=','entity_states.state_id')
->get();
For your reference Laravel Query Builder
Code:
<?php
class Catering extends \Eloquent {
protected $table = 'catering';
public $timestamps = FALSE;
public function offers() {
return $this->hasMany('Offer', 'cid');
}
}
class Offer extends \Eloquent {
protected $table = 'catering_offer';
public $timestamps = FALSE;
public function catering() {
return $this->belongsTo('Catering');
}
}
I am able to do
$offers = Catering::find(1)->offers;
but, the inverse is not working:
$catering = Offer::find(1)->catering;
is always returning NULL. Database has the right values.
Offer table has 2 columns:
primary(id), int(cid)
that references catering.id.
The question:
How can i access the reverse side of this relation?
You said that, I am able to do
$offers = Catering::find(1)->offers;
and in your Catering model you have
public function offers() {
return $this->hasMany('Offer', 'cid');
}
It seems like you've defined a different foreign key here (cid) to use it instead of the default one that laravel basically supposed to use, so, to do the reverse relation you have to do the same thing in your Offer model's catering function
public function catering() {
return $this->belongsTo('Catering', 'cid');
}
In the Laravel Documentation, it says that, you may override the conventional foreign key by passing a second argument to the hasMany method, like
return $this->hasMany('Offer', 'custom_key');
Same way, to define the inverse of the relationship on the Offer model, you can use the belongsTo method, like
return $this->belongsTo('Catering', 'custom_key'); // cid