charliefortune's avatar

Multiple models from a single table

I've got a situation where I'd like to create more than one model from a single table, and would like to know if my approach is valid.

The reason is not to subclass records (e.g. creating car and motorbike models out of a vehicles table) but is a result of a single table being a bit too broad in scope, in my opinion.

The table is report_logs and has evolved into storing several very unrelated bits of data against reports. For example;

These columns relate to a user's self assessment

report_logs.self_assessment_score, report_logs.self_assessment_time, report_logs.self_assessment_comment

These columns are related to report videos

report_logs.video_feedback, report_logs.video_is_viewed

To make things easier for developers and a clearer separation, I want to create the following two models that refer to the report_logs table:

  • ReportSelfAssessment
  • ReportVideo

These would also then come with their own policy gates, service classes, DTO's etc.

It seems like it would be easier to work this way, each model having a more narrow focus. Are there any problems with this approach that I might not have thought of?

0 likes
4 replies
LaryAI's avatar
Level 58

Your approach to creating multiple models from a single table in Laravel can indeed be valid and beneficial for organizing code around specific functionalities or domains, especially when a table encompasses diverse data that logically belongs to different contexts. Here are some considerations and a basic implementation guide for your scenario:

Considerations

  1. Query Complexity: Each model will need to ensure it only deals with its relevant subset of data, which might complicate queries.
  2. Data Integrity: Care must be taken to ensure that operations on one model don’t inadvertently affect data relevant to the other model.
  3. Model Events: Events (like saving, updating) triggered from these models will need careful handling to avoid unexpected behavior.
  4. Migration and Schema Management: All schema changes will affect both models, so coordination is necessary when migrations are run.

Implementation

You can implement this in Laravel by specifying the same table for both models and using global scopes to differentiate the data each model works with.

Step 1: Define the Models

Both models will use the report_logs table, but they will scope queries to their relevant fields.

// ReportSelfAssessment model
namespace App\Models;

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

class ReportSelfAssessment extends Model
{
    protected $table = 'report_logs';

    protected static function booted()
    {
        static::addGlobalScope('selfAssessment', function (Builder $builder) {
            $builder->whereNotNull('self_assessment_score');
        });
    }
}

// ReportVideo model
namespace App\Models;

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

class ReportVideo extends Model
{
    protected $table = 'report_logs';

    protected static function booted()
    {
        static::addGlobalScope('reportVideo', function (Builder $builder) {
            $builder->whereNotNull('video_feedback');
        });
    }
}

Step 2: Usage

When you query each model, Laravel will automatically apply the appropriate global scope, ensuring that each model only deals with its relevant data.

$assessments = ReportSelfAssessment::all(); // Only fetches rows relevant to self assessments
$videos = ReportVideo::all(); // Only fetches rows relevant to videos

Step 3: Consider Overriding Methods

If there are methods that should behave differently based on the model, you can override them in your models. For example, if updating a ReportSelfAssessment should never touch video fields, make sure to enforce this in your model logic.

Conclusion

This approach allows you to keep your models focused and coherent, which is beneficial for maintaining a large codebase. However, be mindful of the complexities introduced by sharing a single table and ensure thorough testing, especially around database operations affecting shared data.

Snapey's avatar

you could have some crossover if you have fields that are not nullable but belong to the other model, so when saving one model, you have to still consider the integrity of the table as a whole. But otherwise should be a valid solution, using the scope idea from Lary.

1 like
PRESTIGE2930's avatar

@Snapey same thing im thinking, he would have to set all columns as nullable or set a default for all columns to avoid this happening

charliefortune's avatar

@prestige2930 @snapey - thanks for the input, that's a good point about the nullables. As it happens, the non-shared cols are all nullable already as a result of the 'shared' nature of the table, so I'm going to proceed with it.

Please or to participate in this conversation.