Model Inheritance in Laravel
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
# Mastering Model Inheritance in Laravel: Handling Multi-Table Hierarchies with Eloquent
As a senior developer working within the Laravel ecosystem, you’ve hit upon a very common and insightful architectural challenge: how to model object-oriented inheritance when your database schema demands a structure that doesn't map perfectly to simple table extensions. The scenario you describe—an abstract `Tool` parent with specialized child models like `Hammer` and `Screwdriver`, each residing in their own distinct tables—is a classic problem that tests the limits of Eloquent’s default inheritance behavior.
This post will explore why standard PHP/Eloquent inheritance struggles here, and provide practical, robust solutions using Laravel's powerful relationship system to achieve the desired polymorphic querying.
## The Challenge: Inheritance vs. Database Structure
When you extend an Eloquent model, Laravel typically assumes a direct one-to-one or one-to-many relationship based on the table structure. If `Hammer` and `Screwdriver` need separate tables because their attributes are unique and unrelated beyond their shared parent concept (`Tool`), simply extending `Tool` won't automatically link them in a way that allows for a single, unified query like `Tools::all()`.
The core issue is that Eloquent’s internal structure is optimized for simplicity. To achieve true polymorphism across separate tables, we need to leverage Eloquent’s relationship capabilities rather than relying solely on class inheritance for data retrieval.
## The Solution: Polymorphic Relationships and Abstract Classes
Instead of forcing the database hierarchy into a strict single-table inheritance model (which works well when all models share the exact same columns), we should embrace a hybrid approach. We use PHP classes for structure, but Eloquent relationships for dynamic querying across tables.
### Step 1: Define the Base Model (The Abstract Concept)
We define the base `Tool` model. Since it doesn't map to a physical table itself, it acts purely as an abstract contract and defines the shared behavior.
```php
// app/Models/Tool.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
abstract class Tool extends Model
{
// Define common relationships or abstract methods here if needed
}
```
### Step 2: Define the Concrete Models (The Specific Tables)
The concrete models (`Hammer`, `Screwdriver`) will extend the base and hold their specific data in their respective tables. Crucially, they must define a relationship back to the parent concept.
```php
// app/Models/Hammer.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Hammer extends Tool
{
// This model maps directly to the 'hammers' table
}
// app/Models/Screwdriver.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Screwdriver extends Tool
{
// This model maps directly to the 'screwdrivers' table
}
```
### Step 3: Implementing Polymorphic Querying (The Key)
To achieve the goal of calling `Tools::all()` and retrieving all instances of all tool types, we must use Eloquent’s polymorphic capabilities. We don't query the base class directly if it has no table; instead, we define a central interface or relationship that points to all potential tools.
The most effective way to achieve your desired result (`Tools::all()`) is by setting up a dedicated **Parent Model** (or a join table) that manages the collection of all related models.
If you want a unified query across separate tables, you need a mechanism where the subclasses point back to a common parent identifier, and the query uses `whereHas` or relationship loading. Since we are dealing with entirely different table structures, the most elegant solution is often defining a central index or using an intermediate model if the relationships are complex.
For simple retrieval of all tools regardless of type:
```php
// In your controller or service layer:
use App\Models\Tool; // The base class concept
// To get ALL records from all tool-related tables:
$allTools = Tool::union([
\App\Models\Hammer::class,
\App\Models\Screwdriver::class
])->get();
// Alternatively, if you establish a generic relationship on the parent:
// If you add a 'parent_type' column to all tables pointing to the model class name,
// Eloquent can query them together via polymorphic relationships.
```
## Conclusion
While true single-table inheritance is restrictive for multi-entity designs, Laravel’s strength lies in its flexibility provided by Eloquent Relationships. By decoupling the structural inheritance (PHP classes) from the data structure (separate database tables), and using polymorphic querying techniques or explicit unions when retrieving collections, you can successfully implement complex model hierarchies. As you build more intricate systems on Laravel, focusing on how relationships map to your database architecture—as emphasized in documentation like [laravelcompany.com](https://laravelcompany.com)—will ensure your application remains scalable, clean, and highly maintainable.