Laravel中的三向枢轴表或复杂关系

时间:2017-11-18 19:51:35

标签: php laravel laravel-5 relationship

我有两张桌子,货物和客户。在现实世界中,客户可以通过三种方式与货物相关:作为票据,目的地和/或来源。

所以我的问题是,我是否有一个包含三列的数据透视表,一个用于shipment_id,一个用于customer_id,另一个用于relationship_type id?或者我有单独的表?我不确定如何最好地接近这一点,因为它是我遇到过的第一种。

2 个答案:

答案 0 :(得分:2)

几周前我遇到了这个问题,我提出了一个解决方案。

  

假设一个客户可以有不同的关系   出货。

首先,您需要一个新的客户角色模型,显然它的模型将是Relation模型。

第一种方法:您可以通过使用多个数据透视表来解决这个问题,但是这不是一个好的数据库设计。我首先解决了这个问题,但是当谈到数据库时,我意识到它不是最佳选择。

第二种方法:您可以通过将数据透视表定义为模型来解决此问题,但即使我知道它的工作原理和解决方案,我也没有尝试过这种方法。

更好的方法:为三个模型使用一个数据透视表。在这种情况下,您必须在定义关系示例时定义数据透视表:

客户模式:

public function relations()
{
    return $this->belongsToMany(Relation::class, 'customer_relation_shippment');
}

关系模型:

public function customers()
{
    return $this->belongsToMany(Relation::class, 'customer_relation_shippment');
}

和其他模型一样。

现在假设您要为客户添加关系。 让我们抓住第一个客户和第一批货物,并说我们想要添加一个关系作为一个比尔:

$customer = Customer::first();

$shipment = Shipment::first();

$relation = Relation::where('name','biller')->get();

$customer->relations()->attach($shipment->id, ['relationship_type'=>$relation->id]);

当然只使用一个数据透视表,对于像CRUD这样的模型执行操作会有点复杂,但当涉及数据库设计/优化时,它当然是正确的选择!请注意,我在处理类似的现实世界问题之后得出了这个结论,并且它使用更多的数据库交互变得更快,然后使用多个数据透视。

答案 1 :(得分:0)

以下是我设计项目的方法。

我认为您甚至不需要数据透视表或多对多关系。

注意:为了清晰起见并避免与User混淆,我将使用Account来引用您称之为Customer的内容。最后,您在评论中使用了customer account

您的货件与三个不同的实体相关。但是,这些实体由数据库中的相同数据模型表示:Account模型。

基本的一对多关系就足够了。

帐户可以有很多货件。货件属于一个帐户。

现在,如何添加关系的“类型”?我们不需要数据透视表,只需添加另一个一对多关系。

  • Account as biller可能包含多个货件,货件属于biller个货件。

  • Account as origin可能包含多个货件,货件属于origin个货件。

  • Account as destination可能包含多个货件,货件属于origin个货件。

要解释一下,这是一个示例代码:

我们有三种模式:UserAccountShipment

让我们从架构开始:

    Schema::create('accounts', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });

    Schema::create('shipments', function (Blueprint $table) {
        $table->increments('id');
        $table->string('from');
        $table->string('to');

        $table->unsignedInteger('biller_id');
        $table->unsignedInteger('origin_id');
        $table->unsignedInteger('destination_id');

        $table->foreign('biller_id')
            ->references('id')->on('accounts');

        $table->foreign('origin_id')
            ->references('id')->on('accounts');

        $table->foreign('destination_id')
            ->references('id')->on('accounts');

        $table->timestamps();
    });

我们有三列引用id表上的accounts

对于模型和关系:

帐户模型:

class Account extends Model
{
    public function billerShipments()
    {
        return $this->hasMany(Shipment::class, 'biller_id');
    }

    public function originShipments()
    {
        return $this->hasMany(Shipment::class, 'origin_id');
    }

    public function destinationShipments()
    {
        return $this->hasMany(Shipment::class, 'destination_id');
    }

    public function users()
    {
        return $this->belongsToMany(User::class);
    }

}

货件型号:

class Shipment extends Model
{
    public function billerAccount()
    {
        return $this->belongsTo(Account::class, 'biller_id');
    }

    public function originAccount()
    {
        return $this->belongsTo(Account::class, 'origin_id');
    }

    public function destinationAccount()
    {
        return $this->belongsTo(Account::class, 'destination_id');
    }

}

创建货件的示例

    $billerAccount = \App\Account::create(['name' => 'account b']);
    $originAccount = \App\Account::create(['name' => 'account a']);
    $destinationAccount = \App\Account::create(['name' => 'account c']);

    $newShipment = \App\Shipment::create([
        'from'           => 'city 1',
        'to'             => 'city 2',
        'biller_id'      => $billerAccount->id,
        'origin_id'      => $originAccount->id,
        'destination_id' => $destinationAccount->id,
    ]);

    echo $billerAccount->billerShipments()->count(); // 1
    echo $originAccount->originShipments()->count(); // 1
    echo $destinationAccount->destinationShipments()->count(); // 1

    echo $newShipment->billerAccount->name === $billerAccount->name; // 1
    echo $newShipment->originAccount->name === $originAccount->name; // 1
    echo $newShipment->destinationAccount->name === $destinationAccount->name; // 1

对于帐户 - 用户关系,它可以是多对多或一对多,具体取决于您的要求。