在两个外键上定义关系从逻辑上讲是没有意义的。在您的用例中,逻辑关系将是
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,可以包含credit或debit作为值。
//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'],
]);
然后您可以根据需要执行各种查询,也可以查询关系存在has,whereHas
$creditTransactions = $account->transactions()->wherePivot('type', 'credit')->get();
$currentDayTransactions = $account->transactions()->whereDate('created_at', now())->get();