ตามที่ OP ระบุไว้ในความคิดเห็นของเขา:การออกแบบฐานข้อมูลได้ถูกตั้งค่าไว้แล้วดังนั้นความสัมพันธ์แบบ Polymorphicของ Laravel จึงไม่น่าจะเป็นตัวเลือกที่นี่
ฉันชอบคำตอบของ Chris Nealเพราะฉันต้องทำสิ่งที่คล้ายกันเมื่อเร็ว ๆ นี้ (เขียนไดร์เวอร์ฐานข้อมูลของตัวเองเพื่อรองรับ Eloquent สำหรับไฟล์ dbase / DBF) และได้รับประสบการณ์มากมายจาก Eloquent ORM ของ Laravel
ฉันได้เพิ่มรสนิยมส่วนตัวของฉันไว้ในนั้นเพื่อให้โค้ดมีไดนามิกมากขึ้นในขณะที่ยังคงทำแผนที่ชัดเจนต่อรุ่น
คุณสมบัติที่รองรับซึ่งฉันทดสอบอย่างรวดเร็ว:
Animal::find(1)
ทำงานตามที่ถามในคำถามของคุณ
Animal::all()
ทำงานได้เช่นกัน
Animal::where(['type' => 'dog'])->get()
จะส่งคืนAnimalDog
-objects เป็นคอลเล็กชัน
- การแมปวัตถุแบบไดนามิกต่อระดับภาษาพูดซึ่งใช้ลักษณะนี้
- ย้อนกลับไปยัง
Animal
-model ในกรณีที่ไม่มีการกำหนดค่าการแมป (หรือการแมปใหม่ปรากฏในฐานข้อมูล)
ข้อเสีย:
- มันเขียนแบบของภายใน
newInstance()
และnewFromBuilder()
ทั้งหมด (คัดลอกและวาง) ซึ่งหมายความว่าหากจะมีการปรับปรุงใด ๆ จากกรอบการทำงานของสมาชิกนี้คุณจะต้องใช้รหัสด้วยมือ
ฉันหวังว่าจะช่วยได้และฉันพร้อมรับข้อเสนอแนะคำถามและกรณีใช้งานเพิ่มเติมในสถานการณ์ของคุณ นี่คือกรณีการใช้งานและตัวอย่างสำหรับมัน:
class Animal extends Model
{
use MorphTrait; // You'll find the trait in the very end of this answer
protected $morphKey = 'type'; // This is your column inside the database
protected $morphMap = [ // This is the value-to-class mapping
'dog' => AnimalDog::class,
'cat' => AnimalCat::class,
];
}
class AnimalCat extends Animal {}
class AnimalDog extends Animal {}
และนี่คือตัวอย่างของวิธีการใช้และใต้ผลลัพธ์ที่เกี่ยวข้อง:
$cat = Animal::find(1);
$dog = Animal::find(2);
$new = Animal::find(3);
$all = Animal::all();
echo sprintf('ID: %s - Type: %s - Class: %s - Data: %s', $cat->id, $cat->type, get_class($cat), $cat, json_encode($cat->toArray())) . PHP_EOL;
echo sprintf('ID: %s - Type: %s - Class: %s - Data: %s', $dog->id, $dog->type, get_class($dog), $dog, json_encode($dog->toArray())) . PHP_EOL;
echo sprintf('ID: %s - Type: %s - Class: %s - Data: %s', $new->id, $new->type, get_class($new), $new, json_encode($new->toArray())) . PHP_EOL;
dd($all);
ซึ่งผลลัพธ์ต่อไปนี้:
ID: 1 - Type: cat - Class: App\AnimalCat - Data: {"id":1,"type":"cat"}
ID: 2 - Type: dog - Class: App\AnimalDog - Data: {"id":2,"type":"dog"}
ID: 3 - Type: new-animal - Class: App\Animal - Data: {"id":3,"type":"new-animal"}
// Illuminate\Database\Eloquent\Collection {#1418
// #items: array:2 [
// 0 => App\AnimalCat {#1419
// 1 => App\AnimalDog {#1422
// 2 => App\Animal {#1425
และในกรณีที่คุณต้องการให้คุณใช้ที่MorphTrait
นี่แน่นอนว่าเป็นรหัสเต็ม:
<?php namespace App;
trait MorphTrait
{
public function newInstance($attributes = [], $exists = false)
{
// This method just provides a convenient way for us to generate fresh model
// instances of this current model. It is particularly useful during the
// hydration of new objects via the Eloquent query builder instances.
if (isset($attributes['force_class_morph'])) {
$class = $attributes['force_class_morph'];
$model = new $class((array)$attributes);
} else {
$model = new static((array)$attributes);
}
$model->exists = $exists;
$model->setConnection(
$this->getConnectionName()
);
$model->setTable($this->getTable());
return $model;
}
/**
* Create a new model instance that is existing.
*
* @param array $attributes
* @param string|null $connection
* @return static
*/
public function newFromBuilder($attributes = [], $connection = null)
{
$newInstance = [];
if ($this->isValidMorphConfiguration($attributes)) {
$newInstance = [
'force_class_morph' => $this->morphMap[$attributes->{$this->morphKey}],
];
}
$model = $this->newInstance($newInstance, true);
$model->setRawAttributes((array)$attributes, true);
$model->setConnection($connection ?: $this->getConnectionName());
$model->fireModelEvent('retrieved', false);
return $model;
}
private function isValidMorphConfiguration($attributes): bool
{
if (!isset($this->morphKey) || empty($this->morphMap)) {
return false;
}
if (!array_key_exists($this->morphKey, (array)$attributes)) {
return false;
}
return array_key_exists($attributes->{$this->morphKey}, $this->morphMap);
}
}