Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Single Table Inheritance Laravel Package

nanigans/single-table-inheritance

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require nanigans/single-table-inheritance
    

    Add the SingleTableInheritance trait to your base model:

    use Nanigans\SingleTableInheritance\SingleTableInheritance;
    
    class BaseModel extends Model
    {
        use SingleTableInheritance;
    }
    
  2. Define Inheritance Hierarchy: Extend your base model with child models:

    class ChildModel extends BaseModel { }
    class AnotherChildModel extends BaseModel { }
    
  3. Configure Discriminator Column (optional): Override getDiscriminatorColumn() in your base model:

    protected function getDiscriminatorColumn(): string
    {
        return 'model_type';
    }
    
  4. First Use Case:

    // Create a child model
    $child = new ChildModel();
    $child->save(); // Automatically sets `model_type` to 'App\ChildModel'
    
    // Retrieve by type
    $child = BaseModel::where('model_type', 'App\ChildModel')->first();
    

Implementation Patterns

Core Workflows

  1. Model Creation & Persistence: The trait automatically populates the discriminator column (model_type by default) with the fully qualified class name. No manual intervention required.

  2. Polymorphic Queries: Use ofType() to filter by model type:

    $childModels = BaseModel::ofType(ChildModel::class)->get();
    
  3. Dynamic Casting: Retrieve and cast models to their concrete types:

    $baseModel = BaseModel::find(1);
    $concreteModel = $baseModel->cast(); // Returns ChildModel instance
    
  4. Integration with Other Traits: Works seamlessly with Laravel’s built-in traits (e.g., SoftDeletes, Timestamps) and third-party traits like Validating or Observables.


Advanced Patterns

  1. Custom Discriminator Values: Override getDiscriminatorValue() to use non-FQCN values (e.g., short class names):

    protected function getDiscriminatorValue(): string
    {
        return class_basename(static::class);
    }
    
  2. Deep Inheritance Hierarchies: Supports multi-level inheritance (e.g., BaseModel → IntermediateModel → ChildModel). The trait resolves the correct type automatically.

  3. API Responses: Use getDiscriminatorValue() in API responses to expose the model type:

    return response()->json([
        'type' => $model->getDiscriminatorValue(),
        'data' => $model->attributes,
    ]);
    
  4. Event Handling: Dispatch polymorphic events by type:

    $model->cast()->dispatchEvent(); // Triggers events for the concrete type
    

Gotchas and Tips

Common Pitfalls

  1. Discriminator Column Mismatch: Ensure the discriminator column exists in your database table. The trait defaults to model_type but can be customized.

  2. Case Sensitivity: The discriminator value is case-sensitive by default. Use strtolower() in getDiscriminatorValue() if case insensitivity is needed:

    protected function getDiscriminatorValue(): string
    {
        return strtolower(class_basename(static::class));
    }
    
  3. Circular Dependencies: Avoid circular inheritance (e.g., A extends B and B extends A). The trait assumes a valid hierarchy.

  4. Mass Assignment: The discriminator column is not mass assignable by default. Explicitly add it to $fillable if needed:

    protected $fillable = ['model_type', 'other_field'];
    

Debugging Tips

  1. Verify Discriminator Values: Check the stored value in the database matches the expected FQCN or custom value:

    $model->getDiscriminatorValue(); // Debug the actual value
    
  2. Cast Failures: If $model->cast() returns null, ensure:

    • The discriminator column is populated.
    • The class exists and is autoloaded.
    • No typos in the class name.
  3. Query Performance: Avoid ofType() with dynamic class names (e.g., $class = $request->input('type'); BaseModel::ofType($class)). This can lead to SQL injection or N+1 queries. Use a whitelist or whereIn():

    $allowedTypes = [ChildModel::class, AnotherChildModel::class];
    $models = BaseModel::whereIn('model_type', $allowedTypes)->get();
    

Extension Points

  1. Custom Discriminator Logic: Override getDiscriminatorColumn() or getDiscriminatorValue() for custom behavior.

  2. Hybrid Inheritance: Combine with Concrete Table Inheritance by adding a table column and overriding getTable():

    protected function getTable(): string
    {
        return $this->table ?? 'shared_table';
    }
    
  3. Serialization: Customize JSON serialization to include/exclude the discriminator:

    public function toArray()
    {
        return array_merge($this->attributes, [
            'type' => $this->getDiscriminatorValue(),
        ]);
    }
    
  4. Testing: Mock the trait in unit tests by overriding cast() or getDiscriminatorValue():

    $model = new BaseModel();
    $model->shouldReceive('getDiscriminatorValue')->andReturn('MockType');
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
babenkoivan/elastic-client
innmind/static-analysis
innmind/coding-standard
datacore/hub-sdk
alengo/sulu-http-cache-bundle
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
imbo/imbo-coding-standard
visualbuilder/filament-lottie
servicioslineaonce/starter-kit
atomcoder/laravel-reorderable
irajul/filament-shadcn-theme
agtp/agtp-php
agtp/mod-php
centraldesktop/protobuf-php