👤 Họ tên: Nguyễn Thùy Trang
🎓 Mã sinh viên: 23010487
Website bán hàng, cho phép người quản lý thêm, xóa, phân loại sản phẩm.
Dự án sử dụng Laravel, MySQL.
git: https://github.com/chanie-t/Project
composer install
npm install
cp .env.example .env
php artisan key:generate
php artisan migrate
Dasboard
CRUD Product
CRUD Category
CRUD Brand
*Product Model:
class Product extends Model {
use HasFactory;
// Các trường được phép gán hàng loạt
protected $fillable = [
'name',
'slug',
'price',
'short_description',
'description',
'category_id',
'brand_id',
'status',
'image',
'quantity'
];
/**
* Quan hệ product thuộc về category
*/
public function category()
{
return $this->belongsTo(Category::class);
}
public function brand()
{
return $this->belongsTo(Brand::class);
}
public function getImageUrlAttribute()
{
if (strpos($this->image, 'https://') === 0 || strpos($this->image, 'http://') === 0) {
return $this->image;
}
return asset(Storage::url($this->image));
}
}
*Brands model:
class Brand extends Model {
use HasFactory;
protected $fillable = [
'name',
'slug',
'description',
];
public function products()
{
return $this->hasMany(Product::class);
} }
*Category model:
class Category extends Model {
use HasFactory;
protected $fillable = [
'name',
'slug',
'description',
];
public function products()
{
return $this->hasMany(Product::class);
} }
*ProductController
class ProductController extends Controller {
public function index(Request $request)
{
$query = Product::with(['category', 'brand']);
if ($request->has('keyword')) {
$search = $request->input('keyword');
$query->where(function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%')
->orWhere('slug', 'like', '%' . $search . '%');
});
}
$products = $query->paginate(10);
return view('admin.products.index', compact('products'));
}
public function show($id)
{
$product = Product::findOrFail($id);
return view('admin.products.show', compact('product'));
}
public function create()
{
$categories = Category::all();
$brands = Brand::all();
return view('admin.products.create', compact('categories', 'brands'));
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:100',
'slug' => 'nullable|string|max:100|unique:products,slug,' . $request->id,
'price' => 'required|numeric|min:0',
'short_description' => 'required|max:200',
'description' => 'nullable|string',
'category_id' => 'required|exists:categories,id',
'brand_id' => 'required|exists:brands,id',
'image' => 'nullable|image|mimes:jpeg,jpg,png,gif,bmp,svg,webp|max:2048',
'quantity' => 'min:0'
]);
if ($request->hasFile('image')) {
$path = $request->file('image')->store('products', 'public');
$validated['image'] = $path;
}
Product::create($validated);
return redirect()->route('products.index')->with('success', 'Tạo sản phẩm thành công!');
}
public function edit(Request $request, $id)
{
$product = Product::findOrFail($id);
$categories = Category::all();
$brands = Brand::all();
return view('admin.products.edit', compact('product', 'categories', 'brands'));
}
public function update(Request $request, $id)
{
$product = Product::findOrFail($id);
$validated = $request->validate([
'name' => 'required|string|max:100',
'slug' => 'nullable|string|max:100|unique:products,slug,' . $product->id,
'price' => 'required|numeric|min:0',
'short_description' => 'required|max:200',
'description' => 'nullable|string',
'category_id' => 'required|exists:categories,id',
'brand_id' => 'required|exists:brands,id',
'image' => 'nullable|image|max:2048', // 2MB
'quantity' => 'min:0',
'status' => 'in:stock,out_of_stock,discontinued'
]);
if ($request->hasFile('image')) {
$path = $request->file('image')->store('products', 'public');
$validated['image'] = $path;
}
$product->update($validated);
return redirect()->route('products.index')->with('success', 'Cập nhật sản phẩm thành công!');
}
public function destroy($id)
{
$product = Product::findOrFail($id);
$product->delete();
return redirect()->route('products.index')->with('success', 'Xóa sản phẩm thành công!');
}
public function getProductByPage(Request $request)
{
$products = Product::paginate(12);
if ($request->ajax()) {
$view = view('partials.product-loop', ['products' => $products])->render();
return response()->json([
'html' => $view,
'hasMore' => $products->hasMorePages(),
]);
}
return view('welcome', compact('products'));
}
public function search(Request $request)
{
$search = $request->input('search');
$products = Product::where('name', 'like', '%' . $search . '%')
->orWhere('slug', 'like', '%' . $search . '%')
->paginate(10);
return view('admin.products.index', compact('products'));
}
// get product by slug
public function getProductBySlug($slug)
{
$product = Product::where('slug', $slug)->with(['category', 'brand'])->firstOrFail();
return view('client.product.show', compact('product'));
} }
BrandsController:
class BrandController extends Controller { public function index(Request $request)
{
$query = Brand::query();
if ($request->has('keyword')) {
$search = $request->input('keyword');
$query->where(function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%')
->orWhere('slug', 'like', '%' . $search . '%');
});
}
$brands = $query->get();
return view('admin.brands.index', compact('brands'));
}
public function create()
{
return view('admin.brands.create');
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:100',
'slug' => 'nullable|string|max:100|unique:brands,slug',
'description' => 'nullable|string',
]);
Brand::create($validated);
return redirect()->route('brands.index')->with('success', 'Tạo thương hiệu thành công!');
}
public function edit($id)
{
$brand = Brand::findOrFail($id);
return view('admin.brands.edit', compact('brand'));
}
public function update(Request $request, $id)
{
$brand = Brand::findOrFail($id);
$validated = $request->validate([
'name' => 'required|string|max:100',
'slug' => 'nullable|string|max:100|unique:brands,slug,' . $brand->id,
'description' => 'nullable|string',
]);
$brand->update($validated);
return redirect()->route('brands.index')->with('success', 'Cập nhật thương hiệu thành công!');
}
public function destroy($id)
{
$brand = Brand::findOrFail($id);
$brand->delete();
return redirect()->route('brands.index')->with('success', 'Xóa thương hiệu thành công!');
} }
*CategoryController:
class CategoryController extends Controller {
public function index(Request $request)
{
$query = Category::query();
if ($request->has('keyword')) {
$search = $request->input('keyword');
$query->where(function ($q) use ($search) {
$q->where('name', 'like', '%' . $search . '%')
->orWhere('slug', 'like', '%' . $search . '%');
});
}
$categories = $query->paginate(10);
return view('admin.categories.index', compact('categories'));
}
public function create()
{
return view('admin.categories.create');
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:100',
'slug' => 'nullable|string|max:100|unique:categories,slug',
'description' => 'nullable|string',
]);
Category::create($validated);
return redirect()->route('categories.index')->with('success', 'Tạo danh mục thành công!');
}
public function edit($id)
{
$category = Category::findOrFail($id);
return view('admin.categories.edit', compact('category'));
}
public function update(Request $request, $id)
{
$category = Category::findOrFail($id);
$validated = $request->validate([
'name' => 'required|string|max:100',
'slug' => 'nullable|string|max:100|unique:categories,slug,' . $category->id,
'description' => 'nullable|string',
]);
$category->update($validated);
return redirect()->route('categories.index')->with('success', 'Cập nhật danh mục thành công!');
}
public function destroy($id)
{
$category = Category::findOrFail($id);
$category->delete();
return redirect()->route('categories.index')->with('success', 'Xóa danh mục thành công!');
} }
Cấu trúc chính của view
Luôn sử dụng phiên bản mới nhất giúp ứng dụng được cải tiến hiệu năng và các tính năng mới nhất
Chống giả mạo yêu cầu CSRF
duyệt qua danh sách danh mục,thấy đúng danh mục và thương hiệu đang được chọn, đồng thời có thể chọn lại cái khác nếu muốn.
bảo vệ và kiểm soát quyền truy cập vào các route
https://github.com/chanie-t/Project
https://chanie-t.github.io/Project/
Trang đăng nhập
Trang đăng ký
Thêm sản phẩm
Xem , sửa và xóa :
Thêm mới:
Sửa và xóa:
Xem sản phẩm:
Thêm mới:
sửa và xóa: