Polymorphic BelongsTo relationship in Laravel - laravel

How could I set relationships to use just one table (model_types) in Laravel to store types for cars and bikes?
Car model
public function carTypes()
{
return $this->hasMany(CarType::class);
}
CarType model (inverse relationship):
public function car()
{
return $this->belongsTo(Car::class);
}
Bike model
public function bikeTypes()
{
return $this->hasMany(BikeType::class);
}
BikeType model (inverse relationship):
public function bike()
{
return $this->belongsTo(Bike::class);
}

There are 2 options I can think of to solve this problem, the first being a simple table using a type column and the other is using polymorphic relations which is a little overkill.
The first option is to have a type column on your model_types table which you could use to determine the type and adding constants in your ModelType class like this:
const TYPE_CAR = 1;
const TYPE_BIKE = 2;
Then you can easily access the data like so, so from the Car model it's
public function modelType()
{
return $this->belongsTo(ModelType::class)->where('type', ModelType::TYPE_CAR);
}
If you wanted to access it from the model_types table it would look like this:
public function cars()
{
return $this->hasMany(Car::class)
}
public function bikes()
{
return $this->hasMany(Bike::class)
}

You have it reversed.
A car can belong to one car type, but one car type can apply to many cars.
The same goes for bikes.
You don't need a polymorphic relationship.
Car model
public function carType()
{
return $this->belongsTo(ModelType::class);
}
Bike model
public function bikeType()
{
return $this->belongsTo(ModelType::class);
}
ModelType model
public function cars()
{
return $this->hasMany(Car::class);
}
public function bikes()
{
return $this->hasMany(Bike::class);
}

Not sure about inverse relationship, but in your Car model you should use
public function carTypes()
{
return $this->hasMany(ModelType::class, 'foreign_key', 'local_key');
}

Car Model:
public function carTypes() {
return $this->hasMany(ModelType::class);
}
Bike Model:
public function bikeTypes() {
return $this->hasMany(ModelType::class);
}
ModelType Model:
public function car() {
return $this->belongsTo(Car::class, 'modeltype_car_id');
}
public function bike() {
return $this->belongsTo(Bike::class, 'modeltype_bike_id');
}

Related

Polymorphic relationship with other polymorphic realtion?

I have order and return tables which have a created_by and accepted_by field where the corresponding user id is stored.
But now I would like to have multiple types for created_by and accepted_by instead of only user type. Assume a company could also create or accept the order/return.
I was thinking of a polymorphic one to many relationship.
Let’s name this table participants.
Something like:
ID
created
participantable_id
participantable_type
1
1
1
user
2
0
11
company
This works either for order or return but not both.
Is it practically to add extra colums to participants like trx_id and trx_type (order/return)?
How would the realtionships looks like to perform queries like:
$order->createdBy // should give me either user or company model
$order->acceptedBy
$return->createdBy
$return->acceptedBy
Or is there even a cleaner solution I am overlooking?
Thanks!
It would be 1:N polymorphic relationships so no need for an extra table. The structure can be found in the documentation
Your Order and Return model need to have the columns created_by_id, created_by_type, accepted_by_id, accepted_by_type.
______ (1) Company
/
Order (N) ----<
\______ (1) User
_____ (1) Company
/
Return (N) ----<
\_____ (1) User
class Order extends Model
{
public function created_by()
{
return $this->morphTo(__FUNCTION__, 'created_by_type', 'created_by_id');
}
public function accepted_by()
{
return $this->morphTo(__FUNCTION__, 'accepted_by_type', 'accepted_by_id');
}
}
class Return extends Model
{
public function created_by()
{
return $this->morphTo(__FUNCTION__, 'created_by_type', 'created_by_id');
}
public function accepted_by()
{
return $this->morphTo(__FUNCTION__, 'accepted_by_type', 'accepted_by_id');
}
}
class User extends Authenticatable
{
public function created_orders()
{
return $this->morphMany(Order::class, 'created_by');
}
public function accepted_orders()
{
return $this->morphMany(Order::class, 'accepted_by');
}
public function created_returns()
{
return $this->morphMany(Return::class, 'created_by');
}
public function accepted_returns()
{
return $this->morphMany(Return::class, 'accepted_by');
}
}
class Company extends Model
{
public function created_orders()
{
return $this->morphMany(Order::class, 'created_by');
}
public function accepted_orders()
{
return $this->morphMany(Order::class, 'accepted_by');
}
public function created_returns()
{
return $this->morphMany(Return::class, 'created_by');
}
public function accepted_returns()
{
return $this->morphMany(Return::class, 'accepted_by');
}
}

nested query with laravel model

I am writing a nested query with Laravel.
Namely First, the Truck information is drawn, then I list the data of the vehicle with "truck_history", there is no problem until here, but I want to show the information of the invoice belonging to the "invoice_id" in truck_history. but I couldn't understand how to query, I want to do it in the model, is this possible? If possible, how will it be done?
"ID" COLUMN IN INVOICE TABLE AND "invoice_id" in "InvoiceDetail" match.
TruckController
public function getTruck($id)
{
$truck = Truck::with(['truckHistory'])->find($id);
return $truck;
}
Truck Model
protected $appends = ['company_name'];
public function companys()
{
return $this->belongsTo(Contact::class, 'company_id', 'id');
}
public function getCompanyNameAttribute()
{
return $this->companys()->first()->name;
}
public function truckHistory(){
return $this->hasMany(InvoiceDetail::class,'plate_no','plate');
}
So you can add another relationship in the InvoiceDetail::class and add in the truck history.
try something like this:
public function truckHistory(){
return $this->hasMany(InvoiceDetail::class,'plate_no','plate')->with('Invoice');
}
Simply add the following relations (if you don't already have them):
Invoice model :
public function truckHistory()
{
return $this->hasOne(InvoiceDetail::class);
}
InvoiceDetail model :
public function invoice()
{
return $this->belongsTo(Invoice::class);
}
And you can get the relation invoice of the relation truckHistory adding a point as separator :
public function getTruck($id)
{
$truck = Truck::with(['truckHistory.invoice'])->find($id);
return $truck;
}

How to filter a polymorphic relationship to only return a specific model?

I'm having an issue with defining a function to filter a polymorphic relationship and only return a specific model. I'll try to explain below:
Say I have three models: A, B, and C. Model A can belong to either of the other two models. Say we're using the polymorphism field name of recipient, so on our model A database schema we have recipient_type and recipient_id.
On model A, I have a the default function called recipient, defined like so:
public function recipient()
{
return $this->morphTo();
}
However, I want a function called b() which will return a relationship so that it can be used with a query builder using the with() function. The idea being I can call $a->b and it will either return an instance of B or null, depending on whether the instance of A belongs to and instance of B...
Sorry this has been a bit of a mouthful..
Appreciate all the help I can get with this one!
Cheers!
You can define it like this
Model A (define accessor)
public function recipient()
{
return $this->morphTo();
}
public function bRelation()
{
return $this->belongsTo(B::class, 'recipient_id', 'id');
}
public function cRelation()
{
return $this->belongsTo(C::class, 'recipient_id', 'id');
}
public function getBAttribute(){ //define accessor
if($this->recipient_type == 'App\B') return $this->bRelation;
return null;
}
public function getCAttribute(){ //define accessor
if($this->recipient_type == 'App\C') return $this->cRelation;
return null;
}
Now use it with eager loading
$records = A::with('bRelation', 'cRelation')->get();
foreach($records as $a){
dd($a->b); //it will return you either instance of `B` or `null`
}
Works well with accessors:
public function getIssueAttribute()
{
if($this->commentable_type == 'issues') return $this->commentable;
return null;
}
public function getProjectAttribute()
{
if($this->commentable_type == 'projects') return $this->commentable;
return null;
}
public function commentable()
{
return $this->morphTo('commentable');
}

Call to undefined relationship [person] on model [App\Note]

i have a notes, cards and person table wich notes table has card_id and person_id
now once i want load card data i alse want load person name's name but laravel give this error: Call to undefined relationship [person] on model [App\Note].
sorry my english is not good.
this my relationships:
notes:
public function card()
{
return $this->blongsTo(Card::class);
}
public function person()
{
return $this->blongsTo(Person::class);
}
cards:
public function notes()
{
return $this->hasMany(Note::class);
}
persons
public function notes()
{
return $this->hasMany(Note::class);
}
and this my action:
public function showCard(Card $card)
{
$card->load('notes.person');
return view('cards.card',compact('card'));
}
whats wrong?
You have a spelling mistake. Check the relationship, it says: ->blongsTo instead of belongsTo.
public function card()
{
return $this->belongsTo(Card::class);
}
public function person()
{
return $this->belongsTo(Person::class);
}

lumen/laravel Eloquent hasManyThrough 3 models relation

I try to acomplish a relation with 3 models:
Cities.php //table cities
id
name
Neighbourhoods.php //table neighbourhoods
id
name
city_id
Blocks.php //table blocks
id
name
neighbourhood_id
My models looks like this:
Cities.php
public function neighbourhoods()
{
return $this->hasManyThrough('App\Neighbourhoods', 'App\Blocks', 'neighbourhood_id', 'city_id', 'id');
}
Neighbourhoods.php
public function blocks()
{
return $this->hasMany('App\Blocks', 'neighbourhood_id', 'id');
}
Blocks.php
public function neighbourhoods()
{
return $this->belongsToMany('App\Neighbourhoods', 'neighbourhood_id', 'id');
}
The result shoud be:
results
city1:
neighbourhoods:
neighbourhood1:
block1
block2
block3
neighbourhood2
block1
block2
city2:
neighbourhoods:
neighbourhood1:
blocks:
block1
block2
block3
neighbourhood2:
blocks:
block1
block2
Calling the results:
return Blocks::with('neighbourhoods')->get();
I know that my models are not properly named. City (singular), Neighbourhood (singlar), Block (singular) but passing parameters shoud work.
I just can't figure our why it does not work.
RELATIONSSHIP SOLUTION based on #Gaurav Rai's response
First of all, my models are wrong named. Please condider naming your database using plurals for example: cities, neighbourhoods, blocks and your models singular for example: City.php, Neighbourhood.php and Block.php
Based on my problem, the solution is:
Cities.php
public function neighbourhoods()
{
return $this->hasMany('App\Neighbourhoods', 'city_id', 'id');
// because my model is called Cities.php,
// the function will look by default
// for the column cities_id in neighbourhoods table,
// thats why we need to specifiy city_id column
}
public function blocks()
{
return $this->hasManyThrough('App\Blocks', 'App\Neighbourhoods', 'city_id', 'neighbourhood_id', 'id');
}
Neighbourhoods.php
public function cities()
{
return $this->belongsTo('App\Cities', 'city_id', 'id');
}
public function blocks()
{
return $this->hasMany('App\Blocks', 'neighbourhood_id','id');
}
Blocks.php
public function neighbourhoods()
{
return $this->belongsTo('App\Neighbourhoods', 'neighbourhood_id');
}
Calling the relation:
return Cities::with(['neighbourhoods', 'blocks'])->get();
I think your relationships are not well defined:
Cities.php
public function neighbourhoods()
{
return $this->hasMany('App\Neighbourhoods');
}
public function blocks()
{
return $this->hasManyThrough('App\Neighbourhoods', 'App\Blocks');
}
Neighbourhoods.php
public function blocks()
{
return $this->hasMany('App\Blocks');//by default it will consider id
}
public function city()
{
return $this->belongsTo('App\City');
}
Blocks.php
public function neighbourhoods()
{
return $this->belongsTo('App\Neighbourhoods');
}

Resources