laraveleloquent-relationship

laravel polymorphic relation with custom FK in related models


Here is what the table might look like

Store

Shoe

Cloth

Bag

The model relationships for the parent model and related models


class Store extends Model
{
    /**
     * Get the store <Cloth|Bag|Shoe>
     */
    public function modelable()
    {
        return $this->morphTo(__FUNCTION__, 'modelable_type', 'modelable_id');
    }
}

class Shoe extends Model
{
    /**
     * Get the store that owns the shoe.
     */
    public function store()
    {
        return $this->morphOne(Store::class, 'modelable');
    }
}

class Cloth extends Model
{
    /**
     * Get the store that owns the cloth.
     */
    public function store()
    {
        return $this->morphOne(Store::class, 'modelable');
    }
}

class Bag extends Model
{
    /**
     * Get the store that owns the bag.
     */
    public function store()
    {
        return $this->morphOne(Store::class, 'modelable');
    }
}

When a store model is retrieved i want it to use

sole_id in Shoe instead of id
jean_id in Cloth instead of id
cover_id in Bag instead of id

by default laravel expects the foreign keys to be named the same thing


Solution

  • I understand what you're trying to do, but it's not a good idea.

    The 4th parameter of the morphto method is $ownerKey. That is the parameter you want to change.

    class Store extends Model
    {
        public function modelable()
        {
            return $this->morphTo(__FUNCTION__, 'modelable_type', 'modelable_id', 'shoe_id');
        }
    }
    

    If you have a Store record that looks like this:

    {
        ...,
        "modelable_type": "App\Models\Shoe",
        "modelable_id": 5,
        ...
    }
    

    Then the query you're going to make every time you load the modelable relationship will be

    SELECT * FROM shoes WHERE shoes.shoe_id IN (5)
    

    The problem? If your modelable_type is not "App\Models\Shoe", then the query stops making any sense. For example, if modelable_type was "App\Models\Cloth" instead, the query would still try to find a shoe_id column.

    SELECT * FROM clothes WHERE clothes.shoe_id IN (5)
    

    You could mitigate this by defining a different morphTo relationship for each modelable_type you're expecting to have but it's not going to be very pretty in the long term.


    If your problem is caused by not having an id column in the shoes table, I'm afraid the only solution I can give that does not involve altering the definition of that table is adding a virtual id column.