I have two tables, banks and Transfers. I am trying to display a transfers table that shows the money transfers between two bank accounts. If I do not use any relationships, then the table shows the appropriate ids, which is means its working fine. The problem comes in is when I want to display the corresponding bank account names. I get an ErrorException: Attempt to read property "accountName" on int.
Can someone have a look:
This is what I currently have: **My Bank Model **
class Bank extends Model
{
use HasFactory;
protected $fillable = [
'accountName',
'bankName',
'currencyCode',
'accountNumber',
'balance',
'contact',
'address',
'created_by',
];
}
My Transfer Model
class Transfer extends Model
{
use HasFactory;
protected $fillable = [
'fromAccount',
'toAccount',
'amount',
'rate',
'date',
'reference',
'description',
'created_by',
];
public function fromAccount()
{
return $this->belongsTo(Bank::class, 'fromAccount');
}
public function toAccount()
{
return $this->belongsTo(Bank::class, 'toAccount');
}
}
**My Transfers Migration **
public function up()
{
Schema::create('transfers', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('fromAccount');
$table->unsignedBigInteger('toAccount');
$table->float('amount', 15, 4);
$table->double('rate', 15, 8);
$table->date('date');
$table->string('reference');
$table->text('description')->nullable();
$table->integer('created_by')->default('0');
$table->timestamps();
$table->foreign('fromAccount')->references('id')->on('banks');
$table->foreign('toAccount')->references('id')->on('banks');
});
}
**My Index Function in the TransferController **
public function index()
{
$transfers = Transfer::with('fromAccount', 'toAccount')->get();
return view('backend.pages.transfers.index', compact('transfers'));
}
**My View blade file **
@foreach ($transfers as $transfer)
<tr>
<td >{{ $transfer->date }}</td>
<td>{{ $transfer->fromAccount->accountName }}</td>
<td>{{ $transfer->toAccount->accountName }}</td>
</tr>
@endforeach
CodePudding user response:
This is a pretty common issue when not using Database conventions for naming. In your case, you've defined the foreign keys in your Migration as:
Schema::create('transfers', function (Blueprint $table) {
$table->unsignedBigInteger('fromAccount');
$table->unsignedBigInteger('toAccount');
// ...
$table->foreign('fromAccount')->references('id')->on('banks');
$table->foreign('toAccount')->references('id')->on('banks');
});
This is fine, but convention suggests that these should be from_account_id
and to_account_id
(or similar). Again, these are "conventions", not hard-enforced rules.
The issue was that you also defined the Relationships with the same name:
public function fromAccount() {
return $this->belongsTo(Bank::class, 'fromAccount');
}
public function toAccount() {
return $this->belongsTo(Bank::class, 'toAccount');
}
Due to ordering of operations, when you call something like $transfer->toAccount
(or ->fromAccount
), you'll get an integer
. You're correctly expecting it to be a Relationship, like $transfer->toAccount->accountName
, but you end up calling something like 1->accountName
(1
being the id
), and that is not valid.
To handle this, you simply need to use unique names for the columns and relationships. I would suggest, once again to match conventions, the following:
Schema::create('transfers', function (Blueprint $table) {
$table->unsignedBigInteger('from_account_id');
$table->unsignedBigInteger('to_account_id');
// ...
$table->foreign('from_account_id')->references('id')->on('banks');
$table->foreign('to_account_id')->references('id')->on('banks');
});
Then in your model:
public function fromAccount() {
return $this->belongsTo(Bank::class, 'from_account_id');
}
public function toAccount() {
return $this->belongsTo(Bank::class, 'to_account_id');
}
Now, your code should work:
@foreach ($transfers as $transfer)
<tr>
<td >{{ $transfer->date }}</td>
<td>{{ $transfer->fromAccount->accountName }}</td>
<td>{{ $transfer->toAccount->accountName }}</td>
</tr>
@endforeach
If you need to reference the id
s, you'd call $transfer->from_account_id
and $transfer->to_account_id
, and your code is no longer "ambiguous".