CRUD Step 1: Plan & Scaffold
Start a complete “Products” feature — the database table, the model and all its routes — with just three commands.
What you will learn
- Plan a CRUD feature from scratch
- Create the migration, model and resource controller
- Understand the 7 RESTful routes Laravel gives you
What we are going to build
Over the next few lessons we will build a small Products manager — a page where you can list, add, edit and delete products. By the end you will have a real, working feature you fully understand. Follow along command-by-command; if you know nothing yet, that is fine — every step is explained.
Each product will have three things: a name, a price, and a description. CRUD apps in Laravel always have the same shape — you create three pieces and Laravel wires the rest:
- A migration — the instructions that build the
productstable in the database. - A model (
Product) — the Eloquent class that reads and writes those rows for you. - A resource controller — one class with a ready-made method for each CRUD action.
Step 1 — make the model and its migration together
Artisan (Laravel’s command tool) can make the model and its migration in one go. The -m flag means “also make a migration for it”.
php artisan make:model Product -mThis creates two files: app/Models/Product.php (the model) and a migration in database/migrations/ named like 2026_01_01_000000_create_products_table.php. The migration is where we describe the table’s columns.
Note: Output: INFO Model [app/Models/Product.php] created successfully. INFO Migration [database/migrations/...createproducts_table.php] created successfully.
Describe the columns in the migration
Open that new migration file and fill the up() method — this is the “build the table” instruction:
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id(); // auto-increment id (1, 2, 3...)
$table->string('name'); // short text (the product name)
$table->decimal('price', 8, 2); // money: up to 8 digits, 2 after the dot
$table->text('description')->nullable(); // long text, allowed to be empty
$table->timestamps(); // created_at + updated_at columns
});
}Line by line: $table->id() makes a unique auto-numbered id; string('name') is a normal text column; decimal('price', 8, 2) stores money safely with two decimal places; text('description')->nullable() is long text that is allowed to be empty (nullable); timestamps() adds the created_at and updated_at columns Laravel manages automatically.
Run the migration
This runs the instruction and actually creates the table in your database:
php artisan migrateNote: Output:
INFO Running migrations.
...createproducts_table .......... DONE
The products table now exists with the columns you defined. Check it in phpMyAdmin if you like.
Let the model accept form data (one important line)
Open app/Models/Product.php and add a $fillable list. This tells Laravel which fields are allowed to be filled from a form — a safety feature called mass-assignment protection.
class Product extends Model
{
protected $fillable = ['name', 'price', 'description'];
}Without $fillable, Product::create() refuses to fill the fields (so a hacker cannot sneak in extra columns). With it, only name, price and description can be set from input — exactly the three we want.
Step 2 — make the controller (all 7 methods ready)
php artisan make:controller ProductController --resourceThe --resource flag is the magic part: it creates app/Http/Controllers/ProductController.php already containing seven empty methods — index, create, store, show, edit, update, destroy — one for each CRUD action. We will fill them in the next lessons.
Step 3 — register all the routes with one line
Open routes/web.php and add these two lines:
use App\Http\Controllers\ProductController;
Route::resource('products', ProductController::class);That single Route::resource line creates seven routes at once — one for every CRUD action, each pointing at the matching controller method. You did not have to write them one by one.
The 7 routes you just got (learn this table once)
| Method | URL | Controller method | What it does |
|---|---|---|---|
| GET | /products | index() | List all products |
| GET | /products/create | create() | Show the “add new” form |
| POST | /products | store() | Save the new product |
| GET | /products/{id} | show() | Show one product |
| GET | /products/{id}/edit | edit() | Show the “edit” form |
| PUT/PATCH | /products/{id} | update() | Save the edits |
| DELETE | /products/{id} | destroy() | Delete the product |
You can see them for real with this command:
php artisan route:list --name=productsNote: Output (the 7 routes): GET|HEAD products .............. products.index POST products .............. products.store GET|HEAD products/create ....... products.create GET|HEAD products/{product} .... products.show PUT|PATCH products/{product} .... products.update DELETE products/{product} .... products.destroy GET|HEAD products/{product}/edit products.edit
Tip: These 7 routes are identical for every resource — products, posts, users, orders. Learn the table once and building any CRUD becomes a fill-in-the-blanks job. Each route also gets a name (like products.create) you will use to build links.
Q. What does a single Route::resource('products', ProductController::class) line create?
✍️ Practice
- Scaffold a second resource called
Task: runmake:model Task -m, add columns,migrate,make:controller TaskController --resource, and addRoute::resource. - Run
php artisan route:listand find your 7 task routes.
🏠 Homework
- Write the 7-route table from memory (method, URL, controller method).