Dưới đây là hướng dẫn thêm – sửa – xóa trong Laravel với ví dụ cụ thể, từng bước từ đầu đến cuối. ví dụ quản lý sản phẩm (Product
) với MySQL.
Lưu ý quan trọng:
- Kế thừa từ bài thức hành số 5, đã có chức năng CRUD trên bảng categories
Nếu chưa có bài lab số 5
Bước 1: hãy tạo CSDL ql_ban_hang
Kết nối CSDL
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE= ql_ban_hang
DB_USERNAME=root
DB_PASSWORD=
Bước 2: Tạo bảng categories
và thêm dữ liệu mẫu cho nó
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
status tinyint(1) NULL DEFAULT(1)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 4. Thêm 10 dòng dữ liệu hoa quả
INSERT INTO categories (name) VALUES
('Táo'),
('Cam'),
('Chuối'),
('Xoài'),
('Dưa hấu'),
('Nho'),
('Ổi'),
('Mận'),
('Dứa'),
('Bưởi');
Bước 3: Tạo mdoe Category
php artisan make:model Category
Nếu bạn đã có model Product rồi thì bỏ qua bước này
php artisan make:model Product -m
database/migrations/xxxx_create_products_table.php
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->boolean('status')->default(1);
$table->decimal('price', 10, 2);
$table->string('image')->nullable();
$table->unsignedBigInteger('category_id');
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->timestamps();
});
}
php artisan migrate
app/Models/Product .php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $fillable = ['name', 'status', 'price', 'image', 'category_id'];
public function category()
{
return $this->belongsTo(Category::class);
}
}
php artisan make:controller ProductController --model=App\Models\Product
routes/web.php
use App\Http\Controllers\ProductController;
Route::resource('products', ProductController::class);
app/Http/Controllers/ProductController.php
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use App\Models\Category;
use Illuminate\Http\Request;
class ProductController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
$data = Product::with('category')->get();
return view('products.index', compact('data'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$categories = Category::orderBy('name')->get();
return view('products.create', compact('categories'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
// Validate dữ liệu
$request->validate([
'name' => 'required',
'price' => 'required|numeric',
'category_id' => 'required|exists:categories,id',
'image' => 'nullable|image|max:2048'
]);
$data = $request->all();
// Xử lý upload ảnh
if ($request->hasFile('image')) {
$fileName = $request->file('image')->hashName();
$request->file('image')->move(public_path('uploads/products'), $fileName);
$data['image'] = $fileName;
}
Product::create($data);
return redirect()->route('products.index')->with('success', 'Thêm sản phẩm thành công');
}
/**
* Display the specified resource.
*/
public function show(Product $product)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Product $product)
{
$categories = Category::orderBy('name')->get();
return view('products.edit', compact('product', 'categories'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Product $product)
{
// validate ữ liệu
$request->validate([
'name' => 'required',
'price' => 'required|numeric',
'category_id' => 'required|exists:categories,id',
'image' => 'nullable|image|max:2048'
]);
$data = $request->all();
if ($request->hasFile('image')) {
$fileName = $request->file('image')->hashName();
$request->file('image')->move(public_path('uploads/products'), $fileName);
$data['image'] = $fileName;
}
$product->update($data);
return redirect()->route('products.index')->with('success', 'Cập nhật sản phẩm thành công');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Product $product)
{
$product->delete();
return redirect()->route('products.index')->with('success', 'Xóa sản phẩm thành công');
}
}
resources/views/products/index.blade.php
@extends('master.main')
@section('body')
<div class="container">
<h1>Danh sách sản phẩm</h1>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Created At</th>
<th>Image</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach($data as $model)
<tr>
<td>{{$model->id}}</td>
<td>{{$model->name}}</td>
<td>{{$model->category ? $model->category->name : ''}}</td>
<td>{{$model->price}} đ</td>
<td>{{$model->created_at}} đ</td>
<td>
<img src="{{url('uploads/'.$model->image)}}" width="50">
</td>
<td>
<form action="{{ route('products.destroy', $model->id)}}" method="POST" role="form">
@csrf @method('DELETE')
<a href="{{ route('products.edit', $model->id)}}" class="btn btn-primary">Sửa</a>
<button type="submit" class="btn btn-danger">Xóa</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@stop()
resources/views/products/create.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<h1>Thêm sản phẩm</h1>
<div class="row">
<div class="col-md-6">
<form action="{{ route('products.store') }}" method="POST" enctype="multipart/form-data">
@csrf @method('PUT')
<div class="form-group">
<label>Tên:</label>
<input type="text" class="form-control" name="name" value="{{old('name')}}">
@error('name') {{$message}} @enderror
</div>
<div class="form-group">
<label>Giá:</label>
<input type="text" class="form-control" name="price" value="{{old('price')}}">
@error('price') {{$message}} @enderror
</div>
<div class="form-group">
<label>Trạng thái:</label>
<select name="status" class="form-control">
<option value="1" @selected(old('status') == 1)>Hiển thị</option>
<option value="0" @selected(old('status') == 0)>Ẩn</option>
</select>
</div>
<div class="form-group">
<label>Danh mục:</label>
<select name="category_id" class="form-control">
@foreach($categories as $c)
<option value="{{ $c->id }}" @selected($c->id == old('category_id'))>{{ $c->name }}</option>
@endforeach
</select>
@error('category_id') {{$message}} @enderror
</div>
<div class="form-group">
<label>Ảnh:</label>
<input type="file" name="image">
@error('image') {{$message}} @enderror
</div>
<button type="submit">Lưu</button>
</form>
</div>
</div>
</div>
@endsection
<a href="{{ route('products.index') }}">Quay lại</a>
resources/views/products/edit.blade.php
@extends('master.main')
@section('body')
<div class="container">
<h1>Chỉnh sửa sản phẩm</h1>
<div class="row">
<div class="col-md-6">
<form action="{{ route('products.update', $product->id) }}" method="POST" enctype="multipart/form-data">
@csrf @method('PUT')
<div class="form-group">
<label>Tên:</label>
<input type="text" class="form-control" name="name" value="{{$product->name}}">
@error('name') {{$message}} @enderror
</div>
<div class="form-group">
<label>Giá:</label>
<input type="text" class="form-control" name="price" value="{{$product->price}}">
@error('price') {{$message}} @enderror
</div>
<div class="form-group">
<label>Trạng thái:</label>
<select name="status" class="form-control">
<option value="1" @selected($product->status == 1)>Hiển thị</option>
<option value="0" @selected($product->status == 0)>Ẩn</option>
</select>
</div>
<div class="form-group">
<label>Danh mục:</label>
<select name="category_id" class="form-control">
@foreach($categories as $c)
<option value="{{ $c->id }}" @selected($c->id == $product->category_id)>{{ $c->name }}</option>
@endforeach
</select>
@error('category_id') {{$message}} @enderror
</div>
<div class="form-group">
<label>Ảnh:</label>
<input type="file" name="image">
@error('image') {{$message}} @enderror
</div>
<button type="submit">Lưu</button>
</form>
</div>
</div>
</div>
@stop()
php artisan serve
Mở: http://127.0.0.1:8000/products
→ Thêm, sửa, xóa sản phẩm.
Mục tiêu: Sinh viên thực hành tạo ứng dụng Laravel quản lý blog, áp dụng kiến thức về migration, model, controller, route, view, và form validation.
Tạo bảng blogs
với các trường sau:
Tên cột | Kiểu dữ liệu | Ghi chú |
---|---|---|
id | BIGINT (PK, AI) | Khóa chính, tự tăng |
name | VARCHAR(255) | Tên blog |
image | DECIMAL(10,2) | Giá blog |
status | BOOLEAN | 1: Còn hàng, 0: Hết hàng |
created_at | TIMESTAMP | Tự sinh bởi Laravel |
updated_at | TIMESTAMP | Tự sinh bởi Laravel |
Sinh viên phải thực hiện đầy đủ các chức năng sau:
Xem danh sách blog
Thêm blog mới
Form nhập gồm: Tên, Giá, Trạng thái (dropdown chọn "Hiển thị hoặc "Tạm ẩn").
Có kiểm tra dữ liệu:
name
: bắt buộc, tối đa 255 ký tự.image
: bắt buộc, định dạng ảnh: jpg. jpeg, png, gif, webmpstatus
: bắt buộc, chỉ nhận 0 hoặc 1.Sau khi thêm thành công quay về danh sách và hiển thị thông báo.
Sửa thông tin blog
Xóa blog
php artisan make:controller ProductController --resource
)php artisan make:model Product -m
)Route::resource('products', ProductController::class)
)