Laravel Eloquent 关系与多个带有“OR”的外键

Posted

技术标签:

【中文标题】Laravel Eloquent 关系与多个带有“OR”的外键【英文标题】:Laravel Eloquent relations with multiple foreign key with "OR" 【发布时间】:2021-04-09 02:03:09 【问题描述】:

我有模型Account,账户有交易-模型Transactions

Transactions 我有两个外键 - from_account_idto_account_id 都与 Account 模型相关。

现在我想在这两个模型之间创建关系(例如... WHERE transactions.from_account_id = accounts.id OR transactions.to_account_id = accounts.id),但 Eloquent 不支持关系上的两个外键。

我找到了topclaudy/compoships这个包,但是不支持OR

有什么解决办法吗?

【问题讨论】:

【参考方案1】:

在两个外键上定义关系从逻辑上讲是没有意义的。在您的用例中,逻辑关系将是

class Account extends Model

    public function outwardTransactions()
    
        return $this->hasMany(Transaction::class, 'from_account_id');
    

    public function inwardTransactions()
    
        return $this->hasMany(Transaction::class, 'to_account_id');
    
 
    public function allTransactions()
    
        return $this->outwardTransactions->concat($this->inwardTransactions);
    

    //Or a local scope on model to get all transactions
    public function scopeWithAllTransactions($query)
    
        return $query->with(['outwardTransactions', 'inwardTransactions']);
    

Transaction 模型上的关系可以定义为

class Transaction extends Model

    public function sourceAccount()
    
        return $this->belongsTo(Account::class, 'from_account_id', 'id');
    

    public function destinationAccount()
    
        return $this->belongsTo(Account::class, 'to_account_id', 'id');
    

    public function accounts()
    
        return collect([$this->sourceAccount, $this->destinationAccount]);
    

    //Or a local scope on model to get all accounts
    public function scopeWithAllAccounts($query)
    
        return $query->with(['sourceAccount', 'destinationAccount']);
    

多对多关系

为了接近您想要实现的目标,一种方法是使用 Account 和 Transaction 模型之间的多对多关系,因为在逻辑上

一笔交易有很多账户(每笔交易有两个账户) 一个账户(可以有)hasMany Transaction(s)

会有一个中间链接表account_transaction,上面会有一个额外的列type,可以包含creditdebit作为值。

//account_transaction table
public function up()

     Schema::create('account_transaction', function (Blueprint $table) 
        $table->id();
        $table->foreignId('account_id')->constrained();
        $table->foreignId('transaction_id')->constrained;
        $table->enum('type', ['debit', 'credit']);
        $table->timestamps();

        $table->unique(['account_id', 'transaction_id']);

    );


//transactions table
public function up()

     Schema::create('transactions', function (Blueprint $table) 
        $table->id();
        $table->unsignedInteger('amount');
        //any other columns...
        $table->timestamps();

    );

那么关系可以定义为

class Account extends Model

    public function transactions()
    
        return $this->belongsToMany(Transaction::class)
            ->withPivot('type')
            ->withTimestamps();
    

    //... rest of the class code


class Transaction extends Model

    public function accounts()
    
        return $this->belongsToMany(Account::class)
            ->withPivot('type')
            ->withTimestamps();
    

    //... rest of the class code

唯一的问题是,在为交易创建条目时,您需要在 account_transaction 表中创建两个对应的条目 - 一个用于具有 debit 类型的 from_account,另一个用于to_account 带有 credit 类型

//Example transaction:
//FromAccountId = 5
//ToAccountId = 20
//Amount = $100

$transaction = Transaction::create(['amount' => 10000]); //100$ x 100 = 10000 cents in database
$transaction->accounts()->attach([
    5 => ['type' => 'debit'],
    20 => ['type' => 'credit'],
]);

然后您可以根据需要执行各种查询,也可以查询关系存在haswhereHas

$creditTransactions = $account->transactions()->wherePivot('type', 'credit')->get();

$currentDayTransactions = $account->transactions()->whereDate('created_at', now())->get();

【讨论】:

你的例子很好,除非你不打算使用 whereHas :)) @LevanTetemadze 从逻辑上讲,Account 和 Transaction 模型之间的关系应该是Many-to-Many - 已更新答案

以上是关于Laravel Eloquent 关系与多个带有“OR”的外键的主要内容,如果未能解决你的问题,请参考以下文章

Laravel/Eloquent - 创建与可能具有或不具有属性的父模型相关的多个子模型的关系

Laravel Eloquent 嵌套关系枢轴与约束

如何使用 Laravel 的 Eloquent 关系与 Eager Loading 连接 3 个表?

使用 sum 与关系 Laravel Eloquent ORM

如何在 laravel eloquent 中查询多个关系

Laravel Eloquent:渴望加载多个嵌套关系