Như OP đã nêu trong các bình luận của mình: Thiết kế cơ sở dữ liệu đã được thiết lập và do đó Mối quan hệ đa hình của Laravel dường như không phải là một lựa chọn ở đây.
Tôi thích câu trả lời của Chris Neal vì tôi đã phải làm một cái gì đó tương tự gần đây (viết Trình điều khiển cơ sở dữ liệu của riêng tôi để hỗ trợ Eloquent cho các tệp dbase / DBF) và đã có được nhiều kinh nghiệm với các phần bên trong của Eloquent ORM của Laravel.
Tôi đã thêm hương vị cá nhân của mình vào đó để làm cho mã động hơn trong khi vẫn giữ ánh xạ rõ ràng cho mỗi mô hình.
Các tính năng được hỗ trợ mà tôi đã nhanh chóng thử nghiệm:
Animal::find(1)
làm việc như đã hỏi trong câu hỏi của bạn
Animal::all()
làm việc tốt
Animal::where(['type' => 'dog'])->get()
sẽ trả về AnimalDog
-objects như một bộ sưu tập
- Ánh xạ đối tượng động trên mỗi lớp hùng biện sử dụng đặc điểm này
- Dự phòng
Animal
-model trong trường hợp không có ánh xạ được cấu hình (hoặc ánh xạ mới xuất hiện trong DB)
Nhược điểm:
- Đó là viết lại nội bộ
newInstance()
và newFromBuilder()
toàn bộ mô hình (sao chép và dán). Điều này có nghĩa là nếu có bất kỳ cập nhật nào từ khung cho các chức năng thành viên này, bạn sẽ cần phải sử dụng mã bằng tay.
Tôi hy vọng nó có ích và tôi sẵn sàng cho bất kỳ đề xuất, câu hỏi và trường hợp sử dụng bổ sung nào trong kịch bản của bạn. Dưới đây là các trường hợp sử dụng và ví dụ cho nó:
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 {}
Và đây là một ví dụ về cách nó có thể được sử dụng và dưới kết quả tương ứng cho nó:
$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);
kết quả như sau:
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
Và trong trường hợp bạn muốn bạn sử dụng thì MorphTrait
tất nhiên đây là mã đầy đủ cho nó:
<?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);
}
}