Learn how to design a scalable database schema for a project board, establish Eloquent relationships, and enforce data integrity with migration constraints.
Previously in this course, we explored Repository Pattern Fundamentals to decouple our data access layer from our business logic. In this lesson, we shift our focus to the foundation of that layer: the database schema. We'll design the core entities for our multi-user project board—Users, Projects, and Tasks—and establish the relational integrity required for a production application.
When starting a project, it's tempting to throw tables together without considering how they interact. However, a production-grade application requires strict constraints to prevent orphaned records and inconsistent states. We aren't just storing data; we are modeling a domain.
For our project board, we have three primary entities:
We will use Laravel's migration system to enforce these relationships at the database level. While Eloquent handles the "what" in our code, the database schema handles the "how" of data integrity.
A project must belong to a user. We'll use a foreign key constraint to ensure that if a user is deleted, their projects are handled according to our business rules (e.g., onDelete('cascade')).
PHPSchema::create('projects', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->string('name'); $table->text('description')->nullable(); $table->timestamps(); });
Tasks belong to a project. By enforcing the project_id foreign key, we guarantee that no task can exist in a vacuum.
PHPSchema::create('tasks', function (Blueprint $table) { $table->id(); $table->foreignId('project_id')->constrained()->onDelete('cascade'); $table->string('title'); $table->boolean('is_completed')->default(false); $table->timestamps(); });
Once the schema is defined, we map these relationships in our models. This allows us to traverse the graph of data easily. For a refresher on these basics, see Introduction to Database Relationships in Laravel.
In the Project model:
PHPpublic function tasks(): HasMany { return $this->hasMany(Task::class); } public function owner(): BelongsTo { return $this->belongsTo(User::class, 'user_id'); }
In the Task model:
PHPpublic function project(): BelongsTo { return $this->belongsTo(Project::class); }
Your task is to extend the schema to support a "priority" level for tasks.
integer column named priority to the tasks table with a default value of 0.Task model to ensure this field is mass-assignable via the $fillable array.constrained() in your migrations. Without it, you lose database-level integrity, making it possible to have "orphaned" tasks that point to non-existent projects.onDelete('cascade') is convenient, be careful. In some production systems, you might prefer onDelete('restrict') to prevent accidental deletion of a project that still contains active tasks.We've moved from abstract requirements to a concrete database structure. By using migrations to define foreign key constraints, we've ensured that our data remains consistent. By mapping these in Eloquent, we’ve prepared our application to handle complex queries efficiently. This domain modeling approach is the bedrock of maintainable Laravel applications.
Up next: We will dive into Advanced Eloquent Scopes and Accessors to keep our queries clean and our model data formatted for the API.
Learn to use DB::transaction to ensure data integrity in your Laravel apps. Prevent partial state updates by wrapping complex operations in atomic blocks.
Read moreFinalize your Task Manager CRUD functionality by implementing secure edit and delete features. Learn how to maintain data integrity in your Laravel application.
Project Board Domain Modeling
Introduction to Laravel Events and Listeners
Asynchronous Processing with Queues
Job Chaining and Batching
Feature Testing Fundamentals
Mocking Services and Repositories in Tests
Testing Events and Jobs
Database Factories and Seeding
API Versioning Strategies
Advanced Request Filtering and Sorting
Handling File Uploads in REST APIs
Real-time Notifications with Broadcasting
Using Observers for Model Lifecycle Hooks
Implementing Policies for Authorization
Customizing Authentication Guards
Rate Limiting API Endpoints
Eloquent Performance Optimization
Caching Strategies for Performance
Using Traits for Code Reuse
Advanced Dependency Injection with Service Providers
Command Line Tools with Artisan
Scheduled Tasks and Cron Jobs
Integrating Third-Party Services
Handling Webhooks
Logging and Monitoring
Database Migrations Best Practices
Advanced Testing: Integration Tests
Testing API Authentication
Code Quality and Static Analysis
Project Structure for Large Applications
Environment and Configuration Management
Deploying Laravel Applications
Database Indexing Strategies
Using Value Objects
Strategy Pattern for Business Rules
Advanced Queue Monitoring
Building a Search API
Handling Concurrency and Race Conditions
API Documentation with OpenAPI
Testing with Test Doubles
Implementing Multi-Tenancy
Refactoring Legacy Code
Using Middleware for Feature Flags
Building Reusable Packages
Performance Profiling
Secure API Design
Event Sourcing Concepts