Learn how to manage your database schema using Laravel migrations. Discover how to create, define, and run schema updates for your Task Manager project.
Previously in this course, we built the UI for our Task Manager in Task Manager: Building the User Interface. Up until now, our "tasks" have been hardcoded arrays in our controller. To build a real application, we need a persistent way to store data.
In this lesson, we’ll move away from static data and start using migrations. Think of migrations as "version control for your database." Instead of manually running SQL queries in a database tool, we write PHP code that describes our table structure, allowing us to share schema changes across teams and environments reliably.
If you’ve ever worked on a project where you had to manually run ALTER TABLE commands, you know the pain of "it works on my machine but not on production." Migrations solve this by providing a programmatic way to define your database structure. Because these files live in your repository, everyone on your team (and your production server) stays in sync.
To start, we need a table for our tasks. Laravel provides an Artisan command to generate a migration file. Open your terminal in your project root and run:
Bashphp artisan make:migration create_tasks_table
Laravel will generate a new file in the database/migrations directory. If you look at the filename, you’ll see it’s prefixed with a timestamp—this ensures Laravel executes them in the correct chronological order.
Open the newly created file. You will see two main methods: up() and down(). The up() method is where we define the changes (creating tables, adding columns), and the down() method is where we reverse them (dropping tables, removing columns).
Inside the up() method, use the Schema facade to define your columns:
PHPuse Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up(): void { Schema::create('tasks', function (Blueprint $table) { $table->id(); #6A9955">// Auto-incrementing primary key $table->string('title'); #6A9955">// A VARCHAR column $table->text('description')->nullable(); #6A9955">// An optional TEXT column $table->boolean('is_completed')->default(false); #6A9955">// A boolean with a default $table->timestamps(); #6A9955">// Creates created_at and updated_at columns }); } public function down(): void { Schema::dropIfExists('tasks'); } };
This code is expressive and readable. We are using the Blueprint object to fluently define our columns. Whether you are using SQLite (which we configured in Setting Up the Local Development Environment) or MySQL, the syntax remains the same.
Defining the file isn't enough; we need to apply it to the database. Run the following command:
Bashphp artisan migrate
Laravel will look at the migrations table in your database to see which files have already been run. It then executes the up() method of any new files it finds. If you ever need to revert a change, you can use php artisan migrate:rollback.
categories table using php artisan make:migration create_categories_table.name string column and a slug string column to the up() method.php artisan migrate to apply the change.php artisan tinker.down() method: Always define how to undo your changes. If you ever need to rollback, you don't want the process to fail because the down() method is empty.Mastering migrations and seeders in Laravel for beginners is a rite of passage for any developer. By treating your database schema as code, you ensure your application remains stable as it evolves. If you're interested in advanced techniques, you might eventually explore Laravel online schema change for zero-downtime deployments.
Up next: We will bridge the gap between our database tables and our code by creating an Eloquent model to interact with our new tasks table.
Learn how to display database data in your Laravel Task Manager. We'll connect your Eloquent models to your Blade views to render real, dynamic tasks.
Read moreLearn how to use database seeding and factories in Laravel to populate your application with realistic dummy data for testing and development.
Understanding Database Migrations
Protecting Routes with Middleware
Understanding CSRF Protection
Preventing Mass Assignment
Task Manager: Securing the Application
Introduction to Route Model Binding
Updating Existing Records
Deleting Records
Using Named Routes
Task Manager: Completing CRUD Functionality
Introduction to Database Relationships
Querying Related Data
Handling File Uploads
Using Flash Messages for User Feedback
Task Manager: Adding Status and Priorities
Introduction to Artisan Commands
Debugging with Laravel Tinker
Understanding Service Providers
Using View Composers
Task Manager: Refactoring for Clean Code
Introduction to Testing
Testing Forms and Validation
Using Database Transactions
Handling Global Exceptions
Preparing for Production
Environment Security Best Practices
Managing Assets in Production
Task Manager: Deployment Preparation