Home > Blockchain >  Laravel 9 Two foreign Keys from the same table
Laravel 9 Two foreign Keys from the same table

Time:01-20

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.

enter image description here

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 ids, you'd call $transfer->from_account_id and $transfer->to_account_id, and your code is no longer "ambiguous".

  • Related