Dự án xây dựng một website chuyên về tư vấn và mua bán ô tô, kết nối giữa người mua và bán, mang đến trải nghiệm giao dịch:
User
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function cart()
{
return $this->hasMany(Cart::class);
}
}
Car
class Car extends Model
{
protected $fillable = [
'image',
'name',
'brand',
'year',
'price',
'note',
];
}
Cart
class Cart extends Model
{
use HasFactory;
protected $fillable = ['user_id', 'car_id', 'quantity', 'status'];
public function user()
{
return $this->belongsTo(User::class);
}
public function car()
{
return $this->belongsTo(Car::class);
}
}
CartItem
class CartItem extends Model
{
use HasFactory;
protected $fillable = ['cart_id', 'car_id', 'quantity'];
public function cart()
{
return $this->belongsTo(Cart::class);
}
public function car()
{
return $this->belongsTo(Car::class);
}
}
ProfileController
class ProfileController extends Controller
{
/**
* Display the user's profile form.
*/
public function edit(Request $request): View
{
return view('profile.edit', [
'user' => $request->user(),
]);
}
/**
* Update the user's profile information.
*/
public function update(ProfileUpdateRequest $request): RedirectResponse
{
$request->user()->fill($request->validated());
if ($request->user()->isDirty('email')) {
$request->user()->email_verified_at = null;
}
$request->user()->save();
return Redirect::route('profile.edit')->with('status', 'profile-updated');
}
/**
* Delete the user's account.
*/
public function destroy(Request $request): RedirectResponse
{
$request->validateWithBag('userDeletion', [
'password' => ['required', 'current_password'],
]);
$user = $request->user();
Auth::logout();
$user->delete();
$request->session()->invalidate();
$request->session()->regenerateToken();
return Redirect::to('/');
}
}
CarController
class CarController extends Controller
{
public function index(Request $request)
{
//Tìm kiếm xe
$search = $request -> input('search');
$query = Car::query();
if($search) {
$query -> where('name', 'like', "%{$search}%")
-> orWhere('brand', 'like', "%{$search}%");
}
//Phân trang (5/1t)
$cars = $query -> orderBy('id', 'desc') -> paginate(5);
$cars -> appends(['search' => $search]);
return view('cars.index', compact('cars', 'search'));
}
public function create() {
return view('cars.create');
}
public function store(Request $request)
{
$validated = $request->validate([
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
'name' => 'required|string|max:255',
'brand' => 'required|string|max:255',
'year' => 'required|digits:4|integer|min:1900|max:' . date('Y'),
'price' => 'required|numeric|min:0',
'note' => 'nullable|string',
]);
// Xử lý upload ảnh nếu có
if ($request->hasFile('image')) {
$path = $request->file('image')->store('cars', 'public');
$validated['image'] = $path;
}
Car::create($validated);
return redirect()->route('cars.index')->with('success', 'Đã thêm thành công một mẫu xe mới!');
}
public function show($id)
{
$car = Car::findOrFail($id);
return view('cars.show', compact('car'));
}
public function edit($id)
{
$car = Car::findOrFail($id);
return view('cars.edit', compact('car'));
}
public function update(Request $request, $id)
{
$car = Car::findOrFail($id);
$validated = $request->validate([
'image' => 'nullable|image',
'name' => 'required|string|max:255',
'brand' => 'required|string|max:255',
'year' => 'required|digits:4|integer|min:1900|max:' . date('Y'),
'price' => 'required|numeric|min:0',
'note' => 'nullable|string',
]);
// Nếu có upload ảnh mới, xóa ảnh cũ rồi lưu ảnh mới
if ($request->hasFile('image')) {
if ($car->image && Storage::disk('public')->exists($car->image)) {
Storage::disk('public')->delete($car->image);
}
$path = $request->file('image')->store('cars', 'public');
$validated['image'] = $path;
}
$car->update($validated);
return redirect()->route('cars.index')->with('success', 'Cập nhật lại thông tin xe thành công!');
}
public function destroy($id)
{
$car = Car::findOrFail($id);
// Xóa ảnh khi xoá xe
if ($car->image && Storage::disk('public')->exists($car->image)) {
Storage::disk('public')->delete($car->image);
}
$car->delete();
return redirect()->route('cars.index')->with('success', 'Mẫu xe đã được xóa thành công');
}
}
CartController
class CartController extends Controller
{
// Thêm xe vào giỏ hàng
public function addToCart($carId)
{
$user = Auth::user();
$existingItem = $user->cart()
->where('car_id', $carId)
->where('status', 'active')
->first();
if ($existingItem) {
$existingItem->increment('quantity');
} else {
$user->cart()->create([
'car_id' => $carId,
'quantity' => 1,
'status' => 'active',
]);
}
return redirect()->back()->with('success', 'Đã thêm xe vào giỏ hàng!');
}
// Hiển thị giỏ hàng
public function index()
{
$user = Auth::user();
$cartItems = $user->cart()
->where('status', 'active')
->with('car')
->get();
return view('cart.index', compact('cartItems'));
}
// Cập nhật số lượng
public function update(Request $request, $id)
{
$request->validate([
'quantity' => 'required|integer|min:1',
]);
$cartItem = Cart::findOrFail($id);
if ($cartItem->user_id !== Auth::id() || $cartItem->status !== 'active') {
abort(403);
}
$cartItem->quantity = $request->quantity;
$cartItem->save();
return redirect()->route('cart.index')->with('success', 'Cập nhật số lượng thành công!');
}
// Xóa khỏi giỏ hàng
public function removeFromCart($id)
{
$cartItem = Cart::findOrFail($id);
if ($cartItem->user_id !== Auth::id() || $cartItem->status !== 'active') {
abort(403);
}
$cartItem->delete();
return redirect()->back()->with('success', 'Đã xóa khỏi giỏ hàng!');
}
// Hiển thị form thanh toán
public function showCheckoutForm()
{
$user = Auth::user();
$cartItems = $user->cart()
->where('status', 'active')
->with('car')
->get();
if ($cartItems->isEmpty()) {
return redirect()->route('cart.index')->with('error', 'Giỏ hàng của bạn đang trống.');
}
$total = $cartItems->sum(fn($item) => $item->quantity * $item->car->price);
return view('cart.checkout', compact('cartItems', 'total'));
}
// Xử lý gửi yêu cầu tư vấn (checkout)
public function checkout(Request $request)
{
$user = Auth::user();
$cartItems = $user->cart()
->where('status', 'active')
->with('car')
->get();
if ($cartItems->isEmpty()) {
return redirect()->route('cart.index')->with('error', 'Giỏ hàng của bạn đang trống.');
}
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email',
'phone' => 'nullable|string|max:20',
'address' => 'nullable|string|max:255',
'message' => 'nullable|string|max:1000',
]);
$data = [
'user' => $user,
'name' => $request->name,
'email' => $request->email,
'phone' => $request->phone,
'address' => $request->address,
'message' => $request->message,
'cartItems' => $cartItems,
'total' => $cartItems->sum(fn($item) => $item->quantity * $item->car->price),
];
Mail::to('daylaaccclone39@gmail.com')->send(new CheckoutMail($data));
$user->cart()->delete();
// Cập nhật trạng thái các item thành 'checked_out'
// foreach ($cartItems as $item) {
// $item->status = 'checked_out';
// $item->save();
// }
return redirect()->route('cart.index')->with('success', 'Yêu cầu tư vấn của bạn đã được gửi. Chúng tôi sẽ liên hệ bạn sớm nhất!');
}
}
ContactController
class ContactController extends Controller
{
public function index()
{
return view('contact.index');
}
public function send(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email',
'subject' => 'required|string|max:255',
'message' => 'required|string',
]);
$contactData = $request->only(['name', 'email', 'subject', 'message']);
Mail::to('daylaaccclone39@gmail.com')->send(new ContactMail($contactData));
return redirect()->back()->with('success', 'Cảm ơn bạn đã liên hệ với chúng tôi, chúng tôi sẽ phản hồi lại bạn sớm!');
}
}
ScheduleController
class ScheduleController extends Controller
{
// Hiển thị form đặt lịch
public function showForm()
{
return view('schedule.form');
}
// Xử lý lưu thông tin đặt lịch
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'phone' => 'required|string|max:20',
'email' => 'nullable|email',
'service' => 'required|string',
'date' => 'required|date|after_or_equal:today',
'note' => 'nullable|string',
]);
// Gửi mail cho admin
Mail::to('daylaaccclone39@gmail.com')->send(new ScheduleNotification($validated));
// Schedule::create($validated);
return redirect()->route('schedule.form')->with('success', 'Đặt lịch thành công! Chúng tôi sẽ phản hồi bạn sớm.');
}
}
// Trang welcome (mặc định)
Route::get('/', fn () => view('welcome'))->name('welcome');
// Dashboard redirect (cần đăng nhập + xác thực email)
Route::get('/dashboard', fn () => redirect()->route('home.homepage'))
->middleware(['auth', 'verified'])
->name('dashboard');
// Trang chính (Home)
Route::get('/home', fn () => view('home.homepage'))->name('home.homepage');
// Trang liên hệ (Contact)
Route::get('/contact', fn () => view('contact.index'))->name('contact');
Route::post('/contact/send', [ContactController::class, 'send'])->name('contact.send');
// =======================
// Quản lý Giỏ hàng (Cart)
// =======================
Route::middleware('auth')->group(function () {
Route::get('/cart', [CartController::class, 'index'])->name('cart.index');
Route::post('/cart/add/{carId}', [CartController::class, 'addToCart'])->name('cart.add');
Route::patch('/cart/{id}', [CartController::class, 'update'])->name('cart.update');
Route::delete('/cart/remove/{cartId}', [CartController::class, 'removeFromCart'])->name('cart.remove');
// Hiển thị form checkout (gửi yêu cầu tư vấn)
Route::get('/cart/checkout', [CartController::class, 'showCheckoutForm'])->name('cart.checkout.form');
// Xử lý submit form checkout gửi mail
Route::post('/cart/checkout', [CartController::class, 'checkout'])->name('cart.checkout.submit');
});
// =======================
// Quản lý Xe (Cars)
// =======================
// Các route cần phân quyền Admin
Route::middleware(['auth', IsAdmin::class])->group(function () {
Route::get('/cars/create', [CarController::class, 'create'])->name('cars.create');
Route::post('/cars', [CarController::class, 'store'])->name('cars.store');
Route::get('/cars/{car}/edit', [CarController::class, 'edit'])->name('cars.edit');
Route::put('/cars/{car}', [CarController::class, 'update'])->name('cars.update');
Route::delete('/cars/{car}', [CarController::class, 'destroy'])->name('cars.destroy');
});
// Các route ai cũng xem được (Danh sách + Chi tiết)
Route::resource('cars', CarController::class)->only(['index', 'show']);
// Đặt lịch
Route::get('/schedule', [ScheduleController::class, 'showForm'])->name('schedule.form');
Route::post('/schedule', [ScheduleController::class, 'store'])->name('schedule.store');
// =======================
// Quản lý Profile Người dùng
// =======================
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
// =======================
// Auth routes (Laravel Breeze / Fortify / Jetstream)
// =======================
require __DIR__.'/auth.php';
CSRF & XSS Token bảo vệ form (ví dụ: car.index
)
@if(Auth::user() && Auth::user()->is_admin)
<a href="" class="btn-edit">Sửa</a>
<form action="" method="POST"
class="inline-block"
onsubmit="return confirm('Bạn có chắc muốn xóa xe này?');">
@csrf
@method('DELETE')
<button type="submit" class="btn-delete">Xóa</button>
</form>
@endif
<form action="" method="POST"
class="inline-block">
@csrf
<button type="submit" class="btn-cart">Mua Bán</button>
</form>
Query Builder chống SQL Injection (ví dụ: CartController
)
public function addToCart($carId)
{
$user = Auth::user();
$existingItem = $user->cart()
->where('car_id', $carId)
->where('status', 'active')
->first();
if ($existingItem) {
$existingItem->increment('quantity');
} else {
$user->cart()->create([
'car_id' => $carId,
'quantity' => 1,
'status' => 'active',
]);
}
return redirect()->back()->with('success', 'Đã thêm xe vào giỏ hàng!');
}
Middleware phân quyền Admin
class IsAdmin
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next)
{
if (!auth()->check() || !auth()->user()->is_admin) {
abort(403, 'Bạn không có quyền truy cập,');
}
return $next($request);
}
}
// Các route cần phân quyền Admin
Route::middleware(['auth', IsAdmin::class])->group(function () {
Route::get('/cars/create', [CarController::class, 'create'])->name('cars.create');
Route::post('/cars', [CarController::class, 'store'])->name('cars.store');
Route::get('/cars/{car}/edit', [CarController::class, 'edit'])->name('cars.edit');
Route::put('/cars/{car}', [CarController::class, 'update'])->name('cars.update');
Route::delete('/cars/{car}', [CarController::class, 'destroy'])->name('cars.destroy');
});
// Các route ai cũng xem được (Danh sách + Chi tiết)
Route::resource('cars', CarController::class)->only(['index', 'show']);
Laravel framework được phát hành theo giấy phép MIT License.