【问题标题】:Laravel Eloquent relations with multiple foreign key with "OR"Laravel Eloquent 关系与多个带有“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

有什么解决办法吗?

【问题讨论】:

    标签: laravel eloquent foreign-keys relationship


    【解决方案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 - 已更新答案
    猜你喜欢
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 2022-01-19
    • 2013-05-15
    • 2014-09-27
    相关资源
    最近更新 更多