Laravel口才中的多表继承

问题描述 投票:2回答:4

对于在PDF文档上进行书写,用户应该能够创建可以在多个Documents中重用的不同“模块”。有普通模块(Module),其属性为name, posX, posY,例如具有所有属性TextModuleModule具有但添加text, font, color, size。这是使用继承通常可以实现的目标。我发现了几种用Eloquent构建单表继承的方法,但这会导致数据库中有很多NULL值,因为所有Module对象都不会具有任何text, font, colorsize。不幸的是,我还没有找到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()来检索任何类型的模块,因此在此示例中为ModuleTextModule。对于返回集合中包含的每个对象,应该可以调用obj->document来检索相应的文档(对于Document::hasMany(Module::class)关系,反之亦然)。目前,我在调用Module时仅收到所有Module::all()对象,而没有任何错误消息。

我的方法走错了轨道吗?

php laravel inheritance eloquent
4个回答
2
投票

而不是为模块的每个特殊情况使用单独的表,我建议使用嵌套集实现。它主要用于网页上的嵌套类别,但理论上可以用于任何类型的父/子关系。看看下面的Laravel-nestedset程序包。


1
投票

[如果您不介意将数据存储为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电子邮件作曲家之前,我曾使用过这种模式。每次管理层要求一个新的字段类型时,我都花了几分钟时间来实现它,而不必创建新的数据库迁移。但这又是因为在这个特定项目中,我并不真正在意数据完整性。


1
投票

您可以使用此页面做进一步参考:Laravel User Types and Polymorphic Relationships


0
投票

感谢@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"
   }
}]
© www.soinside.com 2019 - 2024. All rights reserved.