5 回答

TA貢獻(xiàn)1827條經(jīng)驗 獲得超8個贊
正如OP在他的評論中所說:數(shù)據(jù)庫設(shè)計已經(jīng)設(shè)置好了,因此Laravel的多態(tài)關(guān)系似乎不是這里的一個選項。
我喜歡克里斯·尼爾(Chris Neal)的答案,因為我最近不得不做類似的事情(編寫我自己的數(shù)據(jù)庫驅(qū)動程序來支持dbase / DBF文件的Eloquent),并且在Laravel的雄辯ORM的內(nèi)部積累了豐富的經(jīng)驗。
我添加了我的個人風(fēng)格,使代碼更加動態(tài),同時保持每個模型的顯式映射。
我快速測試的支持功能:
Animal::find(1)
按照您的問題中提出的問題工作Animal::all()
也可以工作Animal::where(['type' => 'dog'])->get()
將返回 -objects 作為集合AnimalDog
使用此特征的每個雄辯類的動態(tài)對象映射
回退到 -model,以防未配置映射(或數(shù)據(jù)庫中出現(xiàn)新映射)
Animal
弊:
它正在重寫模型的內(nèi)部和完全(復(fù)制和粘貼)。這意味著如果從框架到此成員函數(shù)有任何更新,則需要手動采用代碼。
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 {}
這是如何使用它的一個例子,下面是它的相應(yīng)結(jié)果:
$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);
這將產(chǎn)生以下結(jié)果:
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
如果你想使用這里,你當(dāng)然是它的完整代碼: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);
}
}

TA貢獻(xiàn)1789條經(jīng)驗 獲得超8個贊
您可以在拉拉維爾中使用多態(tài)關(guān)系,如官方拉拉維爾文檔中所述。這是你如何做到這一點。
定義給定模型中的關(guān)系
class Animal extends Model{
public function animable(){
return $this->morphTo();
}
}
class Dog extends Model{
public function animal(){
return $this->morphOne('App\Animal', 'animable');
}
}
class Cat extends Model{
public function animal(){
return $this->morphOne('App\Animal', 'animable');
}
}
在這里,您需要在 animals 表中使用兩列,第一列是animable_type,另一列是animable_id以確定在運行時附加到它的模型的類型。
您可以獲取給定的狗或貓模型,
$animal = Animal::find($id);
$anim = $animal->animable; //this will return either Cat or Dog Model
之后,您可以使用實例of檢查對象的類。$anim
如果您在應(yīng)用程序中添加其他動物類型(即狐貍或獅子),則此方法將幫助您進(jìn)行將來的擴展。它將在不更改代碼庫的情況下工作。這是滿足您要求的正確方法。然而,在不使用多態(tài)關(guān)系的情況下,沒有替代方法可以實現(xiàn)多態(tài)性和渴望加載在一起。如果不使用多態(tài)關(guān)系,則最終會得到多個數(shù)據(jù)庫調(diào)用。但是,如果您有一個區(qū)分模式類型的列,則可能是您有一個錯誤的結(jié)構(gòu)化架構(gòu)。我建議你改進(jìn)它,如果你想簡化它以備將來的發(fā)展。
重寫模型的內(nèi)部新實例()和 newFromBuilder() 不是一種好/推薦的方法,一旦你從框架中獲得更新,你必須重新處理它。

TA貢獻(xiàn)1786條經(jīng)驗 獲得超13個贊
我認(rèn)為您可以重寫模型上的方法,并從屬性中檢查類型,然后初始化相應(yīng)的模型。newInstanceAnimal
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.
$modelName = ucfirst($attributes['type']);
$model = new $modelName((array) $attributes);
$model->exists = $exists;
$model->setConnection(
$this->getConnectionName()
);
$model->setTable($this->getTable());
$model->mergeCasts($this->casts);
return $model;
}
您還需要重寫該方法。newFromBuilder
/**
* Create a new model instance that is existing.
*
* @param array $attributes
* @param string|null $connection
* @return static
*/
public function newFromBuilder($attributes = [], $connection = null)
{
$model = $this->newInstance([
'type' => $attributes['type']
], true);
$model->setRawAttributes((array) $attributes, true);
$model->setConnection($connection ?: $this->getConnectionName());
$model->fireModelEvent('retrieved', false);
return $model;
}

TA貢獻(xiàn)1826條經(jīng)驗 獲得超6個贊
如果您真的想這樣做,可以在 Animal 模型中使用以下方法。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Animal extends Model
{
// other code in animal model ....
public static function __callStatic($method, $parameters)
{
if ($method == 'find') {
$model = parent::find($parameters[0]);
if ($model) {
switch ($model->type) {
case 'dog':
return new \App\Dog($model->attributes);
case 'cat':
return new \App\Cat($model->attributes);
}
return $model;
}
}
return parent::__callStatic($method, $parameters);
}
}

TA貢獻(xiàn)1852條經(jīng)驗 獲得超1個贊
我想我知道你在找什么。請考慮使用 Laravel 查詢范圍的優(yōu)雅解決方案,有關(guān)其他信息,請參閱 https://laravel.com/docs/6.x/eloquent#query-scopes:
創(chuàng)建保存共享邏輯的父類:
class Animal extends \Illuminate\Database\Eloquent\Model
{
const TYPE_DOG = 'dog';
const TYPE_CAT = 'cat';
}
創(chuàng)建具有全局查詢作用域和事件處理程序的子級(或多個級):saving
class Dog extends Animal
{
public static function boot()
{
parent::boot();
static::addGlobalScope('type', function(\Illuminate\Database\Eloquent\Builder $builder) {
$builder->where('type', self::TYPE_DOG);
});
// Add a listener for when saving models of this type, so that the `type`
// is always set correctly.
static::saving(function(Dog $model) {
$model->type = self::TYPE_DOG;
});
}
}
(這同樣適用于另一個類,只需替換常量)Cat
全局查詢作用域充當(dāng)默認(rèn)查詢修改,以便類將始終查找包含 的記錄。Dogtype='dog'
假設(shè)我們有 3 條記錄:
- id:1 => Cat
- id:2 => Dog
- id:3 => Mouse
現(xiàn)在調(diào)用將導(dǎo)致 ,因為默認(rèn)查詢范圍將找不到 哪個是 。調(diào)用 和 都將工作,盡管只有最后一個會給你一個實際的 Cat 對象。Dog::find(1)nullid:1CatAnimal::find(1)Cat::find(1)
這種設(shè)置的好處是,您可以使用上面的類來創(chuàng)建如下關(guān)系:
class Owner
{
public function dogs()
{
return $this->hasMany(Dog::class);
}
}
這種關(guān)系會自動只給你所有的動物與(以類的形式)。將自動應(yīng)用查詢范圍。type='dog'Dog
此外,調(diào)用將自動將 設(shè)置為 由于事件掛鉤(請參閱 https://laravel.com/docs/6.x/eloquent#events)。Dog::create($properties)type'dog'saving
請注意,調(diào)用沒有默認(rèn)值,因此在這里您需要手動設(shè)置(這是預(yù)期的)。Animal::create($properties)type
- 5 回答
- 0 關(guān)注
- 161 瀏覽
添加回答
舉報