Using one table for many similar models in Laravel

In one project we have several models that are identical schemas with a similar usage that we wanted to keep in one table to make maintenance easier. They are basically a value list with the model name being the 'type' of the list. An example might be product colors or t-shirt sizes.

Here's how I made a base class that all these models could be extended from and just work 'TM'.

Our lists table has these fields (besides id etc):

  • name
  • label
  • type
  • description
  • sort

The base class looks like this:

    <?php

    namespace App\Models;

    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\Builder ;

    class ListBase extends Model
    {
        protected $table = 'lists';

        protected static function booted()
        {
            /** 'type' will be the name of the derived class without the namespace e.g. 'Diet', or 'Condition' */

            self::creating(function ($model) {
                $model->type = class_basename(static::class);
            });

            self::addGlobalScope('list type scope', function(Builder $builder){
                $builder->where('type', class_basename(static::class));
            });
        }
    }

This adds some code that is automatically run when you create a new row using a derived class, that will use the derived class name for the type field. In addition, the global scope restricts the rows to ones that match the derived classes name.

A derived class looks like this:

    <?php

    namespace App\Models;

    /**
    * Class Color.
    *
    * @package namespace App\Models;
    * @method static where(string $string, string $name)
    */
   class Color extends ListBase
   {
       /**
       * The attributes that are mass assignable.
       *
       * @var array
       */
       protected $fillable = ['name','label','description','sort'];

   }

We can have many of these derived classes, and even though their data is all in the same table, they only 'see' their own rows.