Cvetan's avatar
Level 6

Eloquent pivot class with child model

I have a data structure like this, there is Store model and Currency model. Every Store can have multiple currencies. There is pivot table in between CurrencyStore. CurrencyStore has child collection of Margin models. However I can't access margins to manipulate them anyhow, getting errors. I assume it is because models do not have standard autoincrement ID. CurrencyStore has composite primary key(store_id, currency_id).

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Currency extends Model
{
    public $incrementing = false;

    protected $fillable = [
        'id',
        'default',
        'buying_rate',
        'middle_rate',
        'selling_rate'
    ];

    public function stores(): BelongsToMany
    {
        return $this->belongsToMany(Store::class)
            ->using(CurrencyStore::class)
            ->withPivot(['store_id', 'currency_id'])
            ->withTimestamps();
    }
}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\Pivot;

class CurrencyStore extends Pivot
{
    public $incrementing = false;
    protected $primaryKey = [
        'store_id',
        'currency_id'
    ];

    protected $fillable = [
        'store_id',
        'currency_id',
        'rate'
    ];

    public function margins(): HasMany
    {
        return $this->hasMany(Margin::class);
    }
}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Margin extends Model
{
    protected $fillable = [
        'currency_store_currency_id',
        'currency_store_store_id',
        'purchase_prise_from',
        'purchase_prise_to',
        'retail_value',
        'wholesale_value'
    ];

    public function currencyStore(): BelongsTo
    {
        return $this->belongsTo(CurrencyStore::class);
    }
}

Is there something I am doing wrong?

0 likes
1 reply
LaryAI's avatar
Level 58

To address the issue of accessing the margins relationship from the CurrencyStore pivot model, you need to ensure that the relationships and primary keys are correctly defined. Here are some steps and corrections you can apply:

  1. Composite Primary Key Handling: Laravel's Eloquent does not natively support composite primary keys. However, you can work around this by ensuring that your CurrencyStore pivot model correctly handles the composite key logic.

  2. Defining Relationships: Ensure that the margins relationship in the CurrencyStore model is correctly defined and that the foreign keys are properly set up.

  3. Pivot Model Configuration: Since CurrencyStore is a pivot model, make sure it extends Pivot and not Model.

Here's how you can adjust your models:

CurrencyStore Model

Ensure that the CurrencyStore model correctly defines the margins relationship and handles the composite key logic:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\Pivot;

class CurrencyStore extends Pivot
{
    public $incrementing = false;
    public $timestamps = false; // If you don't have timestamps in the pivot table

    protected $fillable = [
        'store_id',
        'currency_id',
        'rate'
    ];

    public function margins(): HasMany
    {
        return $this->hasMany(Margin::class, 'currency_store_currency_id', 'currency_id')
                    ->where('currency_store_store_id', $this->store_id);
    }
}

Margin Model

Ensure that the Margin model's currencyStore relationship is correctly defined:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Margin extends Model
{
    protected $fillable = [
        'currency_store_currency_id',
        'currency_store_store_id',
        'purchase_prise_from',
        'purchase_prise_to',
        'retail_value',
        'wholesale_value'
    ];

    public function currencyStore(): BelongsTo
    {
        return $this->belongsTo(CurrencyStore::class, 'currency_store_currency_id', 'currency_id')
                    ->where('currency_store_store_id', $this->currency_store_store_id);
    }
}

Additional Considerations

  • Database Schema: Ensure that your database schema supports the relationships. The margins table should have foreign keys that reference the currency_store table's currency_id and store_id.

  • Querying: When querying, make sure to eager load the margins relationship if you need to access it frequently to avoid N+1 query issues.

By ensuring that the relationships are correctly defined and that the composite keys are handled properly, you should be able to access and manipulate the margins associated with each CurrencyStore instance.

Please or to participate in this conversation.