I basically have two models ( dog and cat ) of the same type: pets . The table pets joins all dogs and cats in the database. Now I would like to be able to find a specific pet through the pet id in the PetController. Like so:
$pet = Pet::findOrFail($id); // returns a dog or cat
Tables structure:
┌──────────────┐ ┌───────────┐ ┌───────────┐
│ pets │ │ dogs │ │ cats │
├──────────────┤ ├───────────┤ ├───────────┤
│ id │ │ id │ │ id │
│ related_type │ │ name │ │ name │
│ related_id │ │ eye_color │ │ tail_size │
└──────────────┘ └───────────┘ └───────────┘
Pets table:
┌────┬──────────────┬────────────┐
│ id │ related_type │ related_id │
├────┼──────────────┼────────────┤
│ 1 │ dog │ 1 │
├────┼──────────────┼────────────┤
│ 2 │ dog │ 2 │
├────┼──────────────┼────────────┤
│ 3 │ cat │ 1 │
└────┴──────────────┴────────────┘
I have searched the Laravel docs but none of the relationships seem to fit for this problem. Only the polymorphic relationship would work the other way around, so that I could access the pet model through the dog- or cat-id. But I am looking for a solution that work the other way around. Is there any kind of relationship without needing to use nasty if-else in the PetController manually?
Thank you!
You can define a polymorphic relationship between these 3 models like this
Pet Model
public function related(){
$this->morphTo();
}
Dog Model
public function pets(){
$this->morphMany('App\Pet', 'related');
}
Cat Model
public function pets(){
$this->morphMany('App\Pet', 'related');
}
Now fetch it like this
$pet = Pet::findOrFail($id)->related;
dd($pet); //you will get either cat or dog
Easy create
$dog = Dog::create(['name'=> 'dog1', 'eye_color' => 'gray']);
$dog->pets()->create();
Check details here https://laravel.com/docs/5.6/eloquent-relationships#polymorphic-relations
You need to keep Model namespase in pats table( related_type
column). Add to your PetModel
public function concretePet()
{
return $this->hasOne($this->related_type, 'id', 'related_id');
}
Use:
$pet = Pet::findOrFail($id)->concretePet;
You can create your own trait for this:
app/MorphToModel.php
<?php
namespace App;
trait MorphToModel
{
protected function morphToModel($related, $name = 'related', $foreignKey = 'id')
{
$table = $this->getTable();
return $this->belongsTo($related, $name . '_id', $foreignKey)
->join(
$table,
$table . '.' . $name . '_id',
($model = new $related)->getTable() . '.' . $model->getKeyName()
)->where($table . '.' . $name . '_type', $related);
}
}
Use the trait in your model(s):
app/Pet.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Pet extends Model
{
use MorphToModel;
// ...
public function related()
{
return $this->morphTo();
}
public function cat()
{
return $this->morphToModel(Cat::class);
}
public function dog()
{
return $this->morphToModel(Dog::class);
}
}
Usage:
$pet = Pet::findOrFail($id);
$pet->cat; // A cat model or null.
$pet->dog; // A dog model or null.
$pet->cat() // A cat relationship query builder.
$pet->dog() // A dog relationship query builder.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.