对于在PDF文档上进行书写,用户应该能够创建可以在多个Documents
中重用的不同“模块”。有普通模块(Module
),其属性为name, posX, posY
,例如具有所有属性TextModule
的Module
具有但添加text, font, color, size
。这是使用继承通常可以实现的目标。我发现了几种用Eloquent构建单表继承的方法,但这会导致数据库中有很多NULL
值,因为所有Module
对象都不会具有任何text, font, color
或size
。不幸的是,我还没有找到Eloquent的任何多表继承文档。
这是我到目前为止所拥有的:
class Module extends Model
{
protected $fillable = [
'name', 'posX', 'posY'
];
public function document()
{
return $this->belongsTo('App\Document');
}
}
class TextModule extends Module
{
protected $fillable = [
'text', 'font', 'color', 'size'
];
}
此外,我的方法是创建两个迁移(因为我需要多表继承),并且在create_modules_table
迁移中具有每个公共属性,而我在create_textmodules_table
中添加了每个“特殊”属性。
我希望调用Module::all()
来检索任何类型的模块,因此在此示例中为Module
和TextModule
。对于返回集合中包含的每个对象,应该可以调用obj->document
来检索相应的文档(对于Document::hasMany(Module::class)
关系,反之亦然)。目前,我在调用Module
时仅收到所有Module::all()
对象,而没有任何错误消息。
我的方法走错了轨道吗?
而不是为模块的每个特殊情况使用单独的表,我建议使用嵌套集实现。它主要用于网页上的嵌套类别,但理论上可以用于任何类型的父/子关系。看看下面的Laravel-nestedset程序包。
[如果您不介意将数据存储为json(这样您就知道在那里丢失了数据),我可能会建议使用其他方法。一个非常基本的示例,具有field
文本列,可能是(未经测试的代码):
class Module extends Model
{
protected $fillable = [
'name', 'posX', 'posY', 'field'
];
protected $casts = [
'field' => 'object'
];
public function document()
{
return $this->belongsTo('App\Document');
}
}
class TextModule extends Module
{
protected $appends = [
'text', 'font', 'color', 'size'
];
public function getTextAttribute(): string
{
return $this->field->text;
}
public function setTextAttribute(string $value): void
{
$field = $this->field;
$field->text = $value;
$this->field = $field;
}
// etc...
}
很显然,通过这种方式,您在交换数据完整性以寻求灵活性时,只有在第一个远不如后者重要时,我才建议这样做。例如,在创建html电子邮件作曲家之前,我曾使用过这种模式。每次管理层要求一个新的字段类型时,我都花了几分钟时间来实现它,而不必创建新的数据库迁移。但这又是因为在这个特定项目中,我并不真正在意数据完整性。
您可以使用此页面做进一步参考:Laravel User Types and Polymorphic Relationships
感谢@sss S关于Laravel中多态关系的链接,我终于弄清楚了如何解决我的问题:
模型
class Module extends Model {
public function moduleable() {
return $this->morphTo();
}
}
class TextModule extends Model {
public function module() {
return $this->morphOne('App\Module', 'moduleable');
}
}
迁移
Schema::create('modules', function (Blueprint $table) {
$table->bigIncrements('id');
$table->float('posX');
// ... other fields mentioned above
$table->morphs('moduleable'); // this creates a "moduleable_id" and "moduleable_type" field
$table->timestamps();
});
Schema::create('textmodules', function (Blueprint $table) {
$table->bigIncrements('id');
// ... only the fields that are exclusive for a TextModule (= not in Module, except "id")
});
工厂
$factory->define(TextModule::class, function (Faker $faker) {
return [
// ... fill the "exclusive" fields as usual
];
});
$factory->define(Module::class, function (Faker $faker) {
$moduleables = [
TextModule::class,
// ... to be extended
];
$moduleableType = $faker->randomElement($moduleables);
$moduleable = factory($moduleableType)->create();
return [
// ... the fields exclusive for Module
// add the foreign key for the created "moduleable" (TextModule)
'moduleable_id' => $moduleable->id,
'moduleable_type' => $moduleableType
];
});
Controller
public function index() {
$all = \App\Module::whereHasMorph('moduleable', '*')->with('moduleable')->get();
return response()->json($all);
}
通配符*
将显示按照上述步骤配置的任何特定Module
(例如TextModule,ImageModule)。直接添加->with('moduleable')
将为每个Module
填充“特定”属性。请查看官方Laravel文档中的"Querying Polymorphic Relationships"部分以获取更多信息。
输出
[{
"id":1,
"posX":34.47,
"posY":17.04,
"moduleable_type":"App\\TextModule",
"moduleable_id":1,
"created_at":"2019-12-02 20:08:01",
"updated_at":"2019-12-02 20:08:01",
"moduleable":{
"id":1,
"font":"Arial",
"color":"#94d22e",
"size":12,
"created_at":"2019-12-02 20:08:00",
"updated_at":"2019-12-02 20:08:00"
}
}]