Home > OS >  Laravel How to handle One To Many relationship using Pivot Tables
Laravel How to handle One To Many relationship using Pivot Tables

Time:12-16

I have a new Laravel application that I'm hooking up to a preexisting database. The relationships in this database are pretty much all pivot tables.

Similar to this:

Schema::create('customers', function (Blueprint $table) {
    $table->id();
    $table->string('name');
});

Schema::create('vehicles', function (Blueprint $table) {
    $table->id();
    $table->string('name');
});

Schema::create('customer_vehicle', function (Blueprint $table) {
    $table->id();    
    $table->int('customer_id');
    $table->int('vehicle_id');
});

I've configured models/pivot tables and set up a Many To Many relationship for the customers and vehicles.

Similar to this:

class Customer extends Model
{
    public function vehicles()
    {
        return $this->belongsToMany(
            Vehicle::class,
            CustomerVehiclePivot::class,
            'customer_id',
            'vehicle_id'
        );
    }
}
class Vehicle extends Model
{
    public function customers()
    {
        return $this->belongsToMany(
            Customer::class,
            CustomerVehiclePivot::class,
            'vehicle_id',
            'customer_id'
        );
    }
}

So far this is working, but it doesn't feel quite right. $customer->vehicles() returns the expected results, but a vehicle should only belong to one customer, and the way I'm doing that at the moment is by doing $vehicle->customers()->first().

The actual relationship should be a One To Many. A customer can have many vehicles, but a vehicle should only belong to one customer.

Is there a way to configure the relationship as a One To Many when using a pivot table in order to be able to fetch a vehicle's customer with $vehicle->customer?


Based on @chuck's suggestion, I now have the following for my Vehicle customer method.

class Vehicle extends Model
{
    public function customer()
    {
        return $this->hasOneThrough(
            Customer::class,
            CustomerVehiclePivot::class,
            'vehicle_id',
            'id',
            'id',
            'customer_id'
        );
    }
}

I can now perform the following and get the expected results.

$vehicle->customer; // Returns the vehicle's customer
$customer->vehicles; // Returns the customer's vehicles

I'm now trying to figure out how to use factories with this configuration.

I thought I could do Vehicle::factory()->for(Customer::factory())->create() but I get the error...

Call to undefined method Illuminate\Database\Eloquent\Relations\HasOneThrough::getOwnerKeyName()

So I'm not quite sure how you can create Vehicles for users.

I was successfully able to create users with attached vehicles by using hasAttached.

Customer::factory()
    ->hasAttached(
        Vehicle::factory()->count(3)
    )
    ->create()

I was able to figure out how to use factories to create vehicles for users.

Customer::factory()->create()->vehicles()->attach(Vehicle::factory()->count(3)->create());

CodePudding user response:

Yes, you can use the hasOneThrough() relationship in Laravel to set up a one-to-many relationship between customers and vehicles using a pivot table. Here is an example of how you can set this up:

class Customer extends Model
{
    public function vehicles()
    {
        return $this->hasMany(Vehicle::class);
    }
}

class Vehicle extends Model
{
    public function customer()
    {
        return $this->hasOneThrough(
            Customer::class,
            CustomerVehiclePivot::class,
            'vehicle_id', // Foreign key on the pivot table
            'id', // Local key on the customers table
            'id', // Local key on the vehicles table
            'customer_id' // Foreign key on the pivot table
        );
    }
}
  • Related