Laravel 类策略设计模式 -> 多态关系
Laravel strategy-like design pattern -> polymorphic relationships
在 Laravel 中实现此目标的最佳方法是什么?我特别提到 Laravel 是因为我对 Laravel 还是很陌生。我正在尝试在其中实现它,而且我确信 Laravel 的某个方面我还不熟悉,可以优雅地解决这个问题。
让我用一个简单的例子来说明:
原设计
假设你有一个车型:
class Car extends Model
{
protected $fillable = [
'manufacturer',
'model',
'engine', //json field emulating a table
]
}
非标准化 'engine' 字符串字段包含 json。 json 将始终具有 'type' 属性,但从那里它向各个方向发展。示例(使用我对汽车发动机的不完整知识):
{ "type": "petrol", "attributes":{"base_engine_type":"V-8", "size":"5.7L"...} }
{ "type": "electric", "attributes":{"battery":"Lithium-Ion", "charge_period_hours":"20"...} }
{ "type": "steam", "attributes":{"pressure_bar":"300", "spontanious_steampunk_world_transport_percent":"42"...} }
等等。
用例
Car 模型将有一个 Vue 页面,其中包含各种发动机类型的下拉列表。选择时,将显示特定于引擎类型的组件及其各自的属性。
我的解决方案
- 保持原样
我使用引擎 JsonResource
来模拟控制器和页面之间的通信模型。
- 具有动态添加属性的单个引擎模型,然后将其注入到
Car
模型中。
我只是对此进行了推测。构造函数中添加的属性不反映。但它的要点是:
class Engine extends Model
{
public function __construct(array $attributes=[])
{
parent::__construct($attributes);
foreach ($this->attributes()->get() as $attr) {
$name = $attr->name;
$this->append($name)->$name = $attr->value;
$this->fillable[] = $name;
}
}
public function attributes() {
return $this->hasMany(EngineAttribute::class);
}
}
class EngineAttribute extends Model
{
protected $fillable = [
'name',
'value',
];
}
树林和树木
到目前为止,第一个解决方案效果最好,但违反了 1NF 并且无法适当扩展。第二种解决方案在应用程序中进一步引入了可怕的复杂性,我宁愿避免它。
建议、替代解决方案、对我的问题的建议修改或一般说明将不胜感激。
(编辑:修复了 JSON 示例)
更新
dung ta van:再次感谢您的回答,伙计。但我看到了你的 Laravel 风格,我用更 Laravel 的风格来培养你。这是一段艰苦的旅程。充满了奇怪的特征和可怕的 Pivot 扩展连接模型,这些连接模型由可怕的连接表生成,通过它们的真实 class 名称捕获了许多模型的灵魂。
但幸运的是,我 return 安全地带着一点理智还完好无损,并为我们带来了这个奖品:
polymorphic relationships
我将我发现的这个奇怪的魔法应用到你已经很好的代码中,瞧!它开始转变为:
<?php
class EngineType extends Model {
public function attributes()
{
return $this->morphMany(
EngineAttribute::class,
'attributable'
);
}
}
class EngineAttribute extends Model {
public function attributable()
{
return $this->morphTo();
}
}
class Car extends Model {
public function engineType() {
return $this->hasOne(EngineType::class);
}
}
我在上面引用的 Ottwell 的著作包含了其余的咒语,但是这个未经测试的炼金术将提供这个想法。
在我看来,选项 2 更好,它更清晰且可扩展,但我们可以改进一点,我们可以删除 Engine
class
php 风格
class EngineType {
public $id;
public $typeName;
}
class EngineAttribute {
public $cardId;
public $engineTypeId;
public $name;
public $value;
}
class Car {
public $id;
public $manufacturer;
public $model;
public $engineTypeId;
}
在laravel风格中:
class EngineType extends Model {
}
class EngineAttribute extends Model {
}
class Car extends Model {
public function engineType() {
return $this->hasOne(EngineType::class);
}
public function attributes() {
return $this->hasManyThrough(EngineAttribute::class, EngineType::class, 'card_id', 'engine_type_id', 'id', 'id');
}
public function toJson() {
return json_encode($this);
}
}
在 Laravel 中实现此目标的最佳方法是什么?我特别提到 Laravel 是因为我对 Laravel 还是很陌生。我正在尝试在其中实现它,而且我确信 Laravel 的某个方面我还不熟悉,可以优雅地解决这个问题。
让我用一个简单的例子来说明:
原设计
假设你有一个车型:
class Car extends Model
{
protected $fillable = [
'manufacturer',
'model',
'engine', //json field emulating a table
]
}
非标准化 'engine' 字符串字段包含 json。 json 将始终具有 'type' 属性,但从那里它向各个方向发展。示例(使用我对汽车发动机的不完整知识):
{ "type": "petrol", "attributes":{"base_engine_type":"V-8", "size":"5.7L"...} }
{ "type": "electric", "attributes":{"battery":"Lithium-Ion", "charge_period_hours":"20"...} }
{ "type": "steam", "attributes":{"pressure_bar":"300", "spontanious_steampunk_world_transport_percent":"42"...} }
等等。
用例
Car 模型将有一个 Vue 页面,其中包含各种发动机类型的下拉列表。选择时,将显示特定于引擎类型的组件及其各自的属性。
我的解决方案
- 保持原样
我使用引擎 JsonResource
来模拟控制器和页面之间的通信模型。
- 具有动态添加属性的单个引擎模型,然后将其注入到
Car
模型中。 我只是对此进行了推测。构造函数中添加的属性不反映。但它的要点是:
class Engine extends Model
{
public function __construct(array $attributes=[])
{
parent::__construct($attributes);
foreach ($this->attributes()->get() as $attr) {
$name = $attr->name;
$this->append($name)->$name = $attr->value;
$this->fillable[] = $name;
}
}
public function attributes() {
return $this->hasMany(EngineAttribute::class);
}
}
class EngineAttribute extends Model
{
protected $fillable = [
'name',
'value',
];
}
树林和树木
到目前为止,第一个解决方案效果最好,但违反了 1NF 并且无法适当扩展。第二种解决方案在应用程序中进一步引入了可怕的复杂性,我宁愿避免它。
建议、替代解决方案、对我的问题的建议修改或一般说明将不胜感激。
(编辑:修复了 JSON 示例)
更新
dung ta van:再次感谢您的回答,伙计。但我看到了你的 Laravel 风格,我用更 Laravel 的风格来培养你。这是一段艰苦的旅程。充满了奇怪的特征和可怕的 Pivot 扩展连接模型,这些连接模型由可怕的连接表生成,通过它们的真实 class 名称捕获了许多模型的灵魂。 但幸运的是,我 return 安全地带着一点理智还完好无损,并为我们带来了这个奖品: polymorphic relationships 我将我发现的这个奇怪的魔法应用到你已经很好的代码中,瞧!它开始转变为:
<?php
class EngineType extends Model {
public function attributes()
{
return $this->morphMany(
EngineAttribute::class,
'attributable'
);
}
}
class EngineAttribute extends Model {
public function attributable()
{
return $this->morphTo();
}
}
class Car extends Model {
public function engineType() {
return $this->hasOne(EngineType::class);
}
}
我在上面引用的 Ottwell 的著作包含了其余的咒语,但是这个未经测试的炼金术将提供这个想法。
在我看来,选项 2 更好,它更清晰且可扩展,但我们可以改进一点,我们可以删除 Engine
class
php 风格
class EngineType {
public $id;
public $typeName;
}
class EngineAttribute {
public $cardId;
public $engineTypeId;
public $name;
public $value;
}
class Car {
public $id;
public $manufacturer;
public $model;
public $engineTypeId;
}
在laravel风格中:
class EngineType extends Model {
}
class EngineAttribute extends Model {
}
class Car extends Model {
public function engineType() {
return $this->hasOne(EngineType::class);
}
public function attributes() {
return $this->hasManyThrough(EngineAttribute::class, EngineType::class, 'card_id', 'engine_type_id', 'id', 'id');
}
public function toJson() {
return json_encode($this);
}
}