Project

Project: Website bán hàng

Thông tin cá nhân

👤 Họ tên: Nguyễn Thùy Trang

🎓 Mã sinh viên: 23010487

📝 Mô tả dự án

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.

🧰 Công nghệ sử dụng

🚀 Cài đặt & Chạy thử

git: https://github.com/chanie-t/Project
composer install
npm install
cp .env.example .env
php artisan key:generate
php artisan migrate

Sơ đồ khối

image

⚙️Sơ đồ chức năng

image

🧠Sơ đồ thuật toán

Dasboard image

CRUD Product

image

CRUD Category

image

CRUD Brand

image

Một số Code chính minh họa

Model

*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);
} }

Controller

*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!');
} }

View

Cấu trúc chính của view

image

Security Setup

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

image

Chống giả mạo yêu cầu CSRF

image

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.

image

bảo vệ và kiểm soát quyền truy cập vào các route

image

Link

https://github.com/chanie-t/Project

Github page

https://chanie-t.github.io/Project/

Một số hình ảnh chức năng chính

Xác thực người dùng

Trang đăng nhập

image Trang đăng ký

image

Trang chính

image

CRUD Products

image Thêm sản phẩm

image Xem , sửa và xóa :

image

CRUD Categories

image Thêm mới:

image Sửa và xóa:

image

Xem sản phẩm:

image

CRUD Brands

image Thêm mới:

image sửa và xóa:

image