Accessors on a pivot table in many to many polymorphic - laravel

I have a polymorphic relationship using a pivot table with some simple columns.
The pivot table has created_at and updated_at. Is there anyway I can set Accessors on those, so I can have them formatted in the collection without looping through it?
public function organization()
{
return $this->morphedByMany('App\Organization', 'relationship')->withPivot('relationship_level')->withTimestamps();
}
public function regions()
{
return $this->morphedByMany('App\Region', 'relationship')->withPivot('relationship_level')->withTimestamps();
}
public function groups()
{
return $this->morphedByMany('App\Group', 'relationship')->withPivot('relationship_level')->withTimestamps();
}
This is the code. I want when I try to access ->pivot->created_at to get it formatted in human way.

You can write an accessor on your polymorhic model, i.e.:
public function getFooAttribute()
{
return $this->pivot ? $this->pivot->created_at->format('Y-m-d') : 'pivot not loaded';
}
And access with $model->foo.
Remember that $model should be loaded by its Owner model so you have the pivot loaded, i.e.:
Region::with('polymorphic_models')->find($id);
And you have to define ->withPivot('relationship_level')->withTimestamps(); on both sides of the relationship.

Related

Laravel many-to-many relation required unexpected table name

I have two tables with many-to-many relation
class Psychologist extends Model
{
public function specs()
{
return $this->belongsToMany('App\Models\Spec');
}
public function methods()
{
return $this->belongsToMany('App\Models\Method');
}
}
Its working fine on saving first relation 'App\Models\Spec' $psy->specs()->attach(explode(',',$data['spec'])); with table name 'psychologist_spec' and get exception of incorrect table 'method_psychologist'
instead of 'psychologist_method'
public function save(Request $req)
{
$data = $req->all();
$psy = Psychologist::create($data);
$psy->specs()->attach(explode(',',$data['spec']));
$psy->methods()->attach(explode(',',$data['methods']));
}
I know that i can explicitly indicate table name like
public function methods()
{
return $this->belongsToMany('App\Models\Method', 'psychologist_method');
}
but cant understand why it working in the first case and doesnt in the second
Eloquent will join the two related model names in alphabetical order

Laravel hasManyThrough with ManyToMany pivot

I am making a game and I have users which have facilities and for this I use ManyToMany
user_facilities
-user_id
-facility_id
But each relation must have a facility level, so I've added facility_levels table and each of this levels must be connected to the ManyToMany relation. So user_facilities now looks like this
user_facilities
-user_id
-facility_id
-level_id
level_id is the connections between the facility which the user owns and which level it is.
My question is how do I connect this in the models?
The User model now has this
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities');
}
And Facility
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities');
}
So how do I get the level of the facility which the user owns?
In blade I hope there is a way I can use something like
{{ $user->facility->level->property }}
level is part of the user_facilities table not of facility
Therefore, you should be able to access the level_id from the many to many relationship of user and facility
One thing you can do is to access the immediate table (also called pivot table).
First, edit your relationship to include the extra attributes.
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities')
->withPivot('level_id');
}
public function users()
{ // if you omit this EDIT/UPDATE, you cannot do this:
// $facility->users()->first()->pivot->level_id;
return $this->belongsToMany('App\User', 'user_facilities')
->withPivot('level_id');
}
Take note that when accessing a many to many relationship, Laravel will immediately assign a pivot attribute onto the result which contains details about the pivot table of the two models
Now try accessing the extra column:
$facility = $user->facilities->first();
$level_id = $facility->pivot->level_id;
// now you can use $level_id for finding the level.
$level = Level::find($level_id);
Now, since you can do that, you can also create a model for the many to many relationship of user and facility that will have that property of level_id
Let's create a new model called UserFacility that will extend Pivot.
This will be your Pivot model for many to many relationship of user and facilities.
use Illuminate\Database\Eloquent\Relations\Pivot;
class UserFacility extends Pivot
{
}
Then update your users and facilities relationships as follows.
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities')
->using('App\UserFacility');
}
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities')
->using('App\UserFacility');
}
Notice that using method.
$userfac = $users->facilities->pivot; // <-- pivot will now be an instance of App\UserFacility
echo $userfac->level_id;
Lastly,
If you don't want the pivot attribute name, you can change it using the as method, chain it after the belongsToMany method, like this:
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities')
->as('UFac')
->using('App\UserFacility');
}
$userfac = $users->facilities->UFac; // <-- you can now access the pivot table using the property `UFac`
echo $userfac->level_id;
It may also be possible that your pivot table has a relationship with a level since it has a level_id. Don't worry, it's possible, just add this function in your UserFacility model.
public function level()
{
return $this->belongsTo('App\Level');
}
Now you can do this!
$user->facilities->first()->UFac->level; // <-- this will be an instance of App\Level
source: https://laravel.com/docs/5.5/eloquent-relationships#many-to-many

Laravel | Using Eloquent hasManyThrough

I have a table called invoiceDetails that has item_id as foreign key from another table called items which has category_id as foreign key from table called categories.
I want to do that following using eloquent:
$result = InvoiceDetail::groupBy('item_id')
->selectRaw('sum(qty) as qty, item_id')->with('item', 'category')->get();
but I am getting error:
Call to undefined relationship [category] on model [App\InvoiceDetail].
Here's my relation inside Category model:
public function invoiceDetail() {
return $this->hasManyThrough('App\InvoiceDetail', 'App\Item', 'category_id', 'item_id');
}
Any suggestions?
Not sure you would even need a hasManyThrough relation here, unless you want to fetch all InvoiceDatail objects belonging to all items which in turn belong to the Category. That part is not clear from your question.
But in your example you are fetching items with their category from distinct item_id.
The reason this is not working is because you are trying to fetch the category relation from the InvoiceDetail object, which does not exist.
->with('item', 'category')
You want to load the Category based on the item relation, not based on the InvoiceDetail, try the dot notation (given that you did define the other relations)
->with('item.category')
Relations should be like this:
class InvoiceDetail extends Model
{
public function item()
{
return $this->belongsTo(\App\Item::class);
}
}
class Item extends Model
{
public function invoiceDetails()
{
return $this->hasMany(\App\InvoiceDetail::class);
}
public function category()
{
return $this->belongsTo(\App\Category::class);
}
}
class Category extends Model
{
public function items()
{
return $this->hasMany(\App\Item::class);
}
public function invoiceDetails()
{
return $this->hasManyThrough(\App\InvoiceDetail::class, \App\Item::class, 'category_id', 'item_id');
}
}
You would want to use the hasManyThrough if, for example, you have a Category and you want to load all the InvoiceDetails directly.
dd($category->invoiceDetails);

I can't make some Eloquent relations to work

Let me first explain the relations of my table, and then I will explain what I cannot do.
So, I have an entity called "Company" it has many "Incomes" which has many "IncomeUnitSale" which has one "IncomeUnitSaleUnitPrice"
Company 1->* Income 1->* IncomeUnitSale 1->1 IncomeUnitSaleUnitPrice
Models:
Company Model
public function Income(){
return $this->hasMany('App\Income');
}
Income Model (table has "company_id")
public function Company(){
return $this->belongsTo('App\Company');
}
public function IncomeUnitSale(){
return $this->hasMany('App\IncomeUnitSale');
}
IncomeUnitSale Model (table has "income_id")
public function Income(){
return $this->belongsTo('App\Income');
}
public function IncomeUnitSaleUnitPrice(){
return $this->hasOne('App\IncomeUnitSaleUnitPrice');
}
IncomeUnitSaleUnitPrice (table has "income_unit_sale_id")
public function IncomeUnitSale(){
return $this->belongsTo('App\IncomeUnitSale');
}
What I am trying to do is the following:
$company = Company::where("id","=",1)->first();
$company->Income->IncomeUnitSale->IncomeUnitSaleUnitPrice
But It says its null, it works till $company->Income->IncomeUnitSale but after that doest't show any relation.
Can somebody please tell me what I am doing wrong?
Thank you!
hasMany relationships will always return an Eloquent Collection object. If there are no related records, it will just be an empty Collection. hasOne and belongsTo relationships will always return either the related Model, or null if there is no related model.
Because some of your relationships are hasMany, you have to iterate the returned Collection to be able to go further. So, instead of $company->Income->IncomeUnitSale->IncomeUnitSaleUnitPrice, you have to:
foreach($company->Income as $income) {
foreach($income->IncomeUnitSale as $sale) {
echo $sale->IncomeUnitSaleUnitPrice->price;
}
}

Returning counts of a relationship model in Laravel

I have a model for user and annotations along with a pivot table user_like for storing annotations liked by user. The annotation table is also associated with another model (ranges) through hasMany relationship. I am trying to return all annotations along with its user, ranges and total number of likes.
The code below works for user, ranges and even likes. But, I am only interested in returning the count of likes and not the actual values (i.e. list of users liking the annotation). Is there a way to include just the counts for one of the models from the relations?
Eloquent query:
$annotations = Annotation::with('ranges')
->with('likes')
->with('author')
->where('document_id', $docid)->get()->toArray();
The model:
class Annotation extends Eloquent {
public function ranges()
{
return $this->hasMany('Range');
}
public function author()
{
return $this->belongsTo('User', 'user_id');
}
public function likes()
{
return $this->belongsToMany('User', 'annotation_like');
}
public function countOfLikes()
{
return $this->likes()->count();
}
}
If you want to retrieve count for multiple annotations using eager loading then you need the following 'helper' relation setup:
public function countLikesRelation()
{
return $this->belongsTo('User','annonation_like')->selectRaw('annotation_like, count(*) as count')->groupBy('annotation_like');
}
// then you can access it as such:
$annotations= Annotation::with('countLikesRelation')->get();
$annotations->first()->countLikesRelation->count;
// to make it easier, we create an accessor to the count attribute
public function getLikesCountAttribute()
{
return $this->countLikesRelation->count;
}
//And then, simply use
$annotations->first()->sectionsCount;

Resources