How can I implement single table inheritance using Laravel's Eloquent?
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Implementing Single Table Inheritance in Laravel Eloquent: A Deep Dive
As developers working with relational databases and Object-Relational Mappers (ORMs) like Eloquent, managing data hierarchies efficiently is crucial. When you need to group related entities that share a common base set of attributes but also possess unique characteristics, inheritance patterns become a powerful tool. Today, we are diving into how to implement Single Table Inheritance (STI) using Laravel's Eloquent, specifically for dividing your Post model into specialized types like Article and Question.
Understanding Single Table Inheritance (STI)
Single Table Inheritance is a database design pattern where all classes in an inheritance hierarchy are stored in a single table. This approach simplifies querying across the entire set of related models, which can be highly beneficial for systems where most records share common attributes.
In the context of Laravel and Eloquent, we achieve this not purely through database inheritance (which is handled by the database schema), but by structuring your models and migrations so that a single table holds all potential post types, differentiated by a specific column.
Step-by-Step Implementation with Eloquent
Given your existing Post model structure, the goal is to make Article and Question subtypes of Post. We will use STI by placing all post types into one central posts table and adding a discriminator column to identify the specific type.
1. Database Migration Setup
The first step is defining a single migration for your core table. This table must accommodate all possible post types.
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\CreateTable;
use Illuminate\Support\Facades\Schema;
class create_posts_table extends Migration
{
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('title');
$table->text('description')->nullable();
$table->integer('views')->default(0);
// *** The Discriminator Column for STI ***
$table->enum('post_type', ['article', 'question'])->default('article');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('posts');
}
}
Notice the crucial addition: the post_type column, which uses an enum type to strictly limit the possible values to 'article' or 'question'.
2. Model Structure
You will define your base model and then create the specialized models that extend it. While Eloquent doesn't enforce strict runtime class inheritance like PHP does, we use the structure to guide our querying.
Base Model (Post.php): This model handles the common attributes and defines the relationships you already have.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $table = 'posts'; // Specifies the single table
protected $fillable = ['user_id', 'title', 'description', 'views', 'post_type'];
/* Relationships (as you defined) */
public function user()
{
return $this->belongsTo(User::class);
}
// ... other relationships
}
Subclass Models (Article.php and Question.php): These models will extend the base and define any specialized logic if needed. In a pure STI setup, these models primarily act as aliases for querying convenience.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Article extends Post
{
// You can add article-specific methods here if necessary
}
class Question extends Post
{
// You can add question-specific methods here if necessary
}
3. Utilizing Inheritance for Querying
The real power of this setup comes when querying the data. Instead of fetching every post and manually checking the type, you can leverage Eloquent's features to filter directly:
Fetching a Specific Type:
// Fetch all articles
$articles = \App\Models\Article::all();
// Fetch all questions
$questions = \App\Models\Question::all();
Fetching All Posts (The STI View):
If you need to retrieve everything, you query the base model:
$allPosts = \App\Models\Post::with('user')->get();
foreach ($allPosts as $post) {
if ($post->post_type === 'article') {
// Handle article logic
} elseif ($post->post_type === 'question') {
// Handle question logic
}
}
Best Practices and Conclusion
While the above method successfully implements a form of Single Table Inheritance by using a discriminator column, it’s important to understand the trade-offs. For simpler hierarchies like this, STI is efficient for reads, as you only hit one table.
However, be mindful that true object-oriented inheritance (where methods are inherited directly) is simulated in Eloquent through convention and explicit querying rather than strict runtime class extension. For very complex systems with many unique attributes, consider the Class Table Inheritance pattern (using separate tables for each type linked by foreign keys), which often results in more organized database schemas while still maintaining data integrity.
By structuring your models and migrations thoughtfully, you can leverage Eloquent's power to manage a diverse set of entities efficiently. Remember, good database design is the foundation of scalable applications, and understanding these ORM patterns is key to mastering it. For more detailed insights into advanced Laravel features, always refer back to resources like Laravel Company.