Search code examples
laravelpolymorphismlaravel-8

Laravel Polymorphic Relationships Issue


I'm trying to take advantage of the Laravel's Polymorphic Relationships

I'm building an app where I have 3 tables: products, expenses and hunts.

Each product, expense or hunt belongs to a category. I used to have 3 other different tables product_categories, expenses_categories and hunts_categories but I didn't like doing it this way so I did a little bit of research and found about this Polimorphic Relationships thing but I'm not really sure how to use it.

I've create a new category table with the following columns

$table->id();
$table->string('name');
$table->integer('categorizable_id');
$table->string('categorizable_type');
$table->foreignId('icon_id')->constrained();

My models looks like

class Product extends Model
{
    public function category () {
        return $this->morphOne(Category::class, 'categorizable');
    }
}

class Expense extends Model
{
    public function category () {
        return $this->morphOne(Category::class, 'categorizable');
    }
}

class Hunt extends Model
{
    public function category () {
        return $this->morphOne(Category::class, 'categorizable');
    }
}

class Category extends Model
{
    public function icon () {
        return $this->belongsTo(Icon::class);
    }

    public function categorizable () {
        return $this->morphTo();
    }
}

I only want 5 categories for the expenses table so I'm seeding them directly to the database but products categories and hunt categories are created by the user

In my CategorySeeder.php file I have the following function

public function run()
    {
        DB::table('categories')->insert([
            ['name' => 'Advertising', 'icon_id' => 1, 'categorizable_id' => ?, 'categorizable_type' => 'App\Models\Expense'],
            ['name' => 'Utilities', 'icon_id' => 2],
            ['name' => 'Shopping', 'icon_id' => 3],
            ['name' => 'Payroll', 'icon_id' => 4],
            ['name' => 'Other', 'icon_id' => 5]
        ]);
    }

Since I want those categories before creating any expense. What should I do with categorizable_id column?


Solution

  • You don't need to use polymorphic relationships for this. You can simply have a categories table:

    $table->id();
    $table->string('name');
    $table->foreignId('icon_id')->constrained();
    

    And then have a category_id column on your product, expenses, and hunts tables:

    $table->foreignId('category_id')->constrained();
    

    Each of your Product, Expense, and Hunt models would have a category method:

    public function category()
    {
        return $this->belongsTo(Category::class);
    }
    

    And then your Category model could have something like this:

    public function products()
    {
        return $this->hasMany(Product::class);
    }
    
    public function expenses()
    {
        return $this->hasMany(Expense::class);
    }
    
    public function hunts()
    {
        return $this->hasMany(Hunt::class);
    }
    

    A polymorphic relationship would be useful if you had a Note model, for example, which could belong to either a product or an expense. In that case, you would have the columns notable_type and notable_id on your notes table, and then use morphOne on your Product and Expense models.