Các vấn đề nâng cao của Laravel 8.
Trang 1PHẦN 8: QUẢN LÝ TÀI KHOẢN Lab 1: Đặt vấn đề
VĐ1: Có 2 loại người dùng (khách hàng và quản lý) thì có cần 2 giao diện khác nhau?
VĐ2: Khi người dùng với quyền hạn xác định đã đăng nhập vào thì cách chuyển trang như thế nào? VĐ3: Có cách nào để đổi tên bảng mặc định của Laravel từ “users” thành “nguoidung”?
VĐ4: Có cách nào để đổi tên bảng mặc định “password_resets” thành “khoiphucmatkhau”?
VĐ5: Làm thế nào để cài thêm phương thức xác thực (đăng nhập bằng Google, Facebook) vào hệ thống? VĐ6: Làm thế nào để thêm Captcha xác thực người-máy ở các form công cộng?
Lab 2: Gi ải quyết vấn đề 1: Xây dựng 2 giao diện khác nhau cho 2 loại tài khoản
Giao diện dành cho khách hàng:
- Ở trang chủ Front-end sẽ có một biểu tượng dành cho người dùng:
- Khi click vào, nếu chưa đăng nhập thì chuyển tới trang đăng nhập:
Biên soạn: Nguyễn Hoàng Tùng Giấy phép CC BY-NC 4.0 Quốc tế
Trang 2- Nếu khách hàng chưa có tài khoản thì đăng ký:
- Sau khi đăng ký/đăng nhập thành công thì chuyển tới trang quản lý tài khoản của khách hàng:
Giao diện dành cho quản lý:
- Trang quản lý chỉ có người dùng được cấp quyền quản lý mới truy cập được
Trang 3- Truy cập địa chỉ http://127.0.0.1/larashop/admin , nếu chưa đăng nhập thì chuyển tới trang đăng nhập của admin:
Sau khi đăng nhập, địa chỉ các trang trong Back-end đều bắt đầu bằng /admin/
Các bước giải quyết:
Bước 1: Cập nhật lại routes tại địa chỉ routes/web.php
Trang 4Auth :: routes ();
// Trang chủ
Route :: get ( '/' , [ HomeController ::class, 'getHome' ])-> name ( 'frontend' );
// Trang sản phẩm
Route :: get ( '/san-pham' , [ HomeController ::class, 'getSanPham' ])-> name ( 'frontend.sanpham' );
Route :: get ( '/san-pham/{tenloai_slug}' , [ HomeController ::class, 'getSanPham' ])-> name ( 'frontend.sanpham.danhmuc' );
Route :: get ( '/san-pham/{tenloai_slug}/{tensanpham_slug}' , [ HomeController ::class, 'getSanPham_ChiTiet' ])-> name ( 'frontend.sanpham.chitiet' );
// Trang giỏ hàng
Route :: get ( '/gio-hang' , [ HomeController ::class, 'getGioHang' ])-> name ( 'frontend.giohang' );
Route :: get ( '/gio-hang/them/{tensanpham_slug}' , [ HomeController ::class, 'getGioHang_Them' ])-> name ( 'frontend.giohang.them' );
Route :: get ( '/gio-hang/xoa' , [ HomeController ::class, 'getGioHang_XoaTatCa' ])-> name ( 'frontend.giohang.xoatatca' ); Route :: get ( '/gio-hang/xoa/{row_id}' , [ HomeController ::class, 'getGioHang_Xoa' ])-> name ( 'frontend.giohang.xoa' ); Route :: get ( '/gio-hang/giam/{row_id}' , [ HomeController ::class, 'getGioHang_Giam' ])-> name ( 'frontend.giohang.giam' ); Route :: get ( '/gio-hang/tang/{row_id}' , [ HomeController ::class, 'getGioHang_Tang' ])-> name ( 'frontend.giohang.tang' );
// Trang đặt hàng
Route :: get ( '/dat-hang' , [ HomeController ::class, 'getDatHang' ])-> name ( 'frontend.dathang' );
Route :: post ( '/dat-hang' , [ HomeController ::class, 'postDatHang' ])-> name ( 'frontend.dathang' );
Route :: get ( '/dat-hang-thanh-cong' , [ HomeController ::class, 'getDatHangThanhCong' ])-> name ( 'frontend.dathangthanhcong' );
// Liên hệ
Route :: get ( '/lien-he' , [ HomeController ::class, 'getLienHe' ])-> name ( 'frontend.lienhe' );
// Trang khách hàng
Route :: get ( '/khach-hang/dang-ky' , [ HomeController ::class, 'getDangKy' ])-> name ( 'khachhang.dangky' );
Route :: get ( '/khach-hang/dang-nhap' , [ HomeController ::class, 'getDangNhap' ])-> name ( 'khachhang.dangnhap' );
// Trang tài khoản khách hàng
Route :: prefix ( 'khach-hang' )-> group (function()
// Trang chủ tài khoản khách hàng
Route :: get ( '/' , [ KhachHangController ::class, 'getHome' ])-> name ( 'khachhang' );
// Xem và cập nhật trạng thái đơn hàng
Route :: get ( '/don-hang/{id}' , [ KhachHangController ::class, 'getDonHang' ])-> name ( 'khachhang.donhang' );
Route :: post ( '/don-hang/{id}' , [ KhachHangController ::class, 'postDonHang' ])-> name ( 'khachhang.donhang' );
// Cập nhật thông tin tài khoản
Route :: post ( '/cap-nhat-ho-so' , [ KhachHangController ::class, 'postCapNhatHoSo' ])-> name ( 'khachhang.capnhathoso' );
});
// Trang tài khoản quản lý
Route :: prefix ( 'admin' )-> group (function()
// Trang chủ tài khoản quản lý
Route :: get ( '/' , [ AdminController ::class, 'getAdmin' ])-> name ( 'admin' );
// Quản lý Loại sản phẩm
Route :: get ( '/loaisanpham' , [ LoaiSanPhamController ::class, 'getDanhSach' ])-> name ( 'loaisanpham' );
Route :: get ( '/loaisanpham/them' , [ LoaiSanPhamController ::class, 'getThem' ])-> name ( 'loaisanpham.them' );
Route :: post ( '/loaisanpham/them' , [ LoaiSanPhamController ::class, 'postThem' ])-> name ( 'loaisanpham.them' );
Route :: get ( '/loaisanpham/sua/{id}' , [ LoaiSanPhamController ::class, 'getSua' ])-> name ( 'loaisanpham.sua' );
Route :: post ( '/loaisanpham/sua/{id}' , [ LoaiSanPhamController ::class, 'postSua' ])-> name ( 'loaisanpham.sua' );
Route :: get ( '/loaisanpham/xoa/{id}' , [ LoaiSanPhamController ::class, 'getXoa' ])-> name ( 'loaisanpham.xoa' );
// Quản lý Sản phẩm
Route :: get ( '/sanpham' , [ SanPhamController ::class, 'getDanhSach' ])-> name ( 'sanpham' );
Route :: get ( '/sanpham/them' , [ SanPhamController ::class, 'getThem' ])-> name ( 'sanpham.them' );
Route :: post ( '/sanpham/them' , [ SanPhamController ::class, 'postThem' ])-> name ( 'sanpham.them' );
Route :: get ( '/sanpham/sua/{id}' , [ SanPhamController ::class, 'getSua' ])-> name ( 'sanpham.sua' );
Route :: post ( '/sanpham/sua/{id}' , [ SanPhamController ::class, 'postSua' ])-> name ( 'sanpham.sua' );
Route :: get ( '/sanpham/xoa/{id}' , [ SanPhamController ::class, 'getXoa' ])-> name ( 'sanpham.xoa' );
Route :: post ( '/sanpham/nhap' , [ SanPhamController ::class, 'postNhap' ])-> name ( 'sanpham.nhap' );
Route :: get ( '/sanpham/xuat' , [ SanPhamController ::class, 'getXuat' ])-> name ( 'sanpham.xuat' );
// Quản lý Đơn hàng
Route :: get ( '/donhang' , [ DonHangController ::class, 'getDanhSach' ])-> name ( 'donhang' );
Route :: get ( '/donhang/them' , [ DonHangController ::class, 'getThem' ])-> name ( 'donhang.them' );
Route :: post ( '/donhang/them' , [ DonHangController ::class, 'postThem' ])-> name ( 'donhang.them' );
Route :: get ( '/donhang/sua/{id}' , [ DonHangController ::class, 'getSua' ])-> name ( 'donhang.sua' );
Route :: post ( '/donhang/sua/{id}' , [ DonHangController ::class, 'postSua' ])-> name ( 'donhang.sua' );
Route :: get ( '/donhang/xoa/{id}' , [ DonHangController ::class, 'getXoa' ])-> name ( 'donhang.xoa' );
// Quản lý Đơn hàng chi tiết
Route :: get ( '/donhang/chitiet' , [ DonHangChiTietController ::class, 'getDanhSach' ])-> name ( 'donhang.chitiet' );
Route :: get ( '/donhang/chitiet/sua/{id}' , [ DonHangChiTietController ::class, 'getSua' ])-> name ( 'donhang.chitiet.sua' );
Route :: post ( '/donhang/chitiet/sua/{id}' , [ DonHangChiTietController ::class, 'postSua' ])-> name ( 'donhang.chitiet.sua' );
Route :: get ( '/donhang/chitiet/xoa/{id}' , [ DonHangChiTietController ::class, 'getXoa' ])-> name ( 'donhang.chitiet.xoa' );
// Quản lý Tài khoản người dùng
Route :: get ( '/nguoidung' , [ UserController ::class, 'getDanhSach' ])-> name ( 'nguoidung' );
Route :: get ( '/nguoidung/sua/{id}' , [ UserController ::class, 'getSua' ])-> name ( 'nguoidung.sua' );
Route :: post ( '/nguoidung/sua/{id}' , [ UserController ::class, 'postSua' ])-> name ( 'nguoidung.sua' );
Route :: get ( '/nguoidung/xoa/{id}' , [ UserController ::class, 'getXoa' ])-> name ( 'nguoidung.xoa' );
});
Bước 2: Cập nhật getDangKy, getDangNhap vào app/Http/Controllers/HomeController.php
<?php
namespace App\Http\Controllers;
Trang 6public function getDatHangThanhCong()
{
return view('frontend.dathangthanhcong');
}
}
Bước 3: Chỉnh sửa app/Http/Controllers/KhachHangController.php
Tập tin KhachHangController.php có thể tạo thủ công bằng copy/paste hoặc dùng lệnh artisan:
php artisan make:controller KhachHangController
Nội dung tập tin KhachHangController.php:
'name' => 'required', 'string', 'max:100'],
'email' => 'required', 'string', 'email', 'max:255', 'unique:nguoidung,email,' $id], 'password' => 'confirmed'],
]);
$orm NguoiDung::find($id);
$orm->name = $request->name;
$orm->username = Str::before($request->email, '@');
$orm->email = $request->email;
if(!empty($request->password)) $orm->password = Hash::make($request->password);
Trang 7dangky.blade.php s ử dụng theme register.html
dangnhap.blade.php s ử dụng theme login.html
khachhang.blade.php s ử dụng theme my-account.html
Lab 3: Gi ải quyết vấn đề 2: Chuyển trang sau khi đăng nhập
Bổ sung code theo hướng dẫn ở Phần 7
Bước 1: Chỉnh sửa app/Providers/RouteServiceProvider.php
RouteServiceProvider quy định các thông số về URL
Chúng ta sẽ chỉnh lại một chút so với bản gốc, thêm hằng số ADMIN, USER và chỉnh lại giá trị hằng số
HOME
public const HOME = '/';
public const USER = '/khach-hang';
public const ADMIN = '/admin';
Bước 2: Chỉnh sửa app/Http/Middleware/RedirectIfAuthenticated.php
Tập tin này quy định: “Nếu đăng nhập thành công thì chuyển tới trang nào?”
public function handle(Request $request, Closure $next, $guards)
{
$guards empty($guards) ? [null] : $guards;
foreach $guards as $guard)
{
if Auth::guard($guard)->check() && Auth::user()->role == 'admin')
return redirect(RouteServiceProvider::ADMIN);
if Auth::guard($guard)->check() && Auth::user()->role == 'user')
return redirect(RouteServiceProvider::USER);
}
return $next($request);
}
Lab 4: Gi ải quyết vấn đề 3 và 4: Đổi tên bảng (table) xác thực mặc định của Laravel
Mặc định, bảng xác thực được Laravel tạo sẵn trong thư mục database/migrations:
Kịch bản:
- Đổi tên bảng users thành nguoidung
- Đổi tên bảng password_resets thành khoiphucmatkhau
Trang 8Bước 1: Chỉnh sửa và bổ sung các tập tin migrations
$table->string('name');
$table->string('username', 100)->unique(); // Tên đăng nhập
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->string('role', 20)->default('user'); // Quyền hạn: admin, user
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrentOnUpdate();
$table->engine = 'InnoDB';
Schema::create('khoiphucmatkhau', function Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
$table->engine = 'InnoDB';
Trang 9- Chỉnh sửa các tập tin migrations có liên kết khóa ngoại tới bảng users vừa đổi, đó là bảng donhang
public function up()
{
Schema::create('donhang', function Blueprint $table) {
$table->id();
$table->foreignId('nguoidung_id')->constrained('nguoidung');
$table->string('dienthoaigiaohang', 20);
$table->string('diachigiaohang');
$table->tinyInteger('tinhtrang')->default( );
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrentOnUpdate();
$table->engine = 'InnoDB';
});
}
Lưu ý: Chạy lệnh php artisan migrate:refresh để cập nhật lại CSDL
Bước 2: Chỉnh sửa tập tin cấu hình xác thực config/auth.php
| This option controls the default authentication "guard" and password
| reset options for your application You may change these defaults
| as required, but they're a perfect start for most applications
| Next, you may define every authentication guard for your application
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider
|
| All authentication drivers have a user provider This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data
| All authentication drivers have a user provider This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table These sources may then
| be assigned to any extra authentication guards you have defined
|
Trang 10| Supported: "database", "eloquent"
| - |
| You may specify multiple password reset configurations if you have more | than one user table or model in the application and you want to have | separate password reset settings based on the specific user types |
| The expire time is the number of minutes that the reset token should be | considered valid This security feature keeps tokens short-lived so | they have less time to be guessed You may change this as needed
| - |
| Here you may define the amount of seconds before a password confirmation | times out and the user is prompted to re-enter their password via the | confirmation screen By default, the timeout lasts for three hours |
*/
'password_timeout' => 10800,
];
Bước 3: Chỉnh sửa tập tin bên trong app/Models
Đổi tên User.php thành NguoiDung.php
Nội dung tập tin NguoiDung.php:
Trang 11return new MailMessage)
->subject('Khôi phục mật khẩu')
->line('Bạn vừa yêu cầu ' config('app.name') ' khôi phục mật khẩu của mình.')
->line('Xin vui lòng nhấn vào nút "Khôi phục mật khẩu" bên dưới để tiến hành cấp mật khẩu mới.') ->action('Khôi phục mật khẩu', url(config('app.url') route('password.reset', $this->token, false))) ->line('Nếu bạn không yêu cầu đặt lại mật khẩu, xin vui lòng không làm gì thêm và báo lại cho quản trị
protected $table 'donhang';
// protected $primaryKey = 'id';
// protected $keyType = 'string';
Trang 12| This controller handles the registration of new users as well as their
| validation and creation By default this controller uses a trait to
| provide this functionality without requiring any additional code
return Validator::make($data, [
'name' => 'required', 'string', 'max:255'],
'email' => 'required', 'string', 'email', 'max:255', 'unique:nguoidung'],
'password' => 'required', 'string', 'min:8', 'confirmed'],
return NguoiDung::create([
'name' => $data['name'],
'username' => Str::before($data['email'], '@'),
'email' => $data['email'],
'password' => Hash::make($data['password']),
Trang 13$nguoidung NguoiDung::all();
return view('nguoidung.danhsach', ['nguoidung' => $nguoidung]);
'name' => 'required', 'string', 'max:100'],
'email' => 'required', 'string', 'email', 'max:255', 'unique:nguoidung'],
'role' => 'required'],
'password' => 'required', 'min:4', 'confirmed'],
]);
$orm new NguoiDung();
$orm->name = $request->name;
$orm->username = Str::before($request->email, '@');
$orm->email = $request->email;
$orm->password = Hash::make($request->password);
$orm->role = $request->role;
$nguoidung NguoiDung::find($id);
return view('nguoidung.sua', ['nguoidung' => $nguoidung]);
'name' => 'required', 'string', 'max:100'],
'email' => 'required', 'string', 'email','max:255', 'unique:nguoidung,email,' $request->id], 'role' => 'required'],
'password' => 'confirmed'],
]);
$orm NguoiDung::find($request->id);
$orm->name = $request->name;
$orm->username = Str::before($request->email, '@');
$orm->email = $request->email;
$orm->role = $request->role;
if(!empty($request->password)) $orm->password = Hash::make($request->password);
// Trang tài khoản quản lý
Route::prefix('admin')->group(function()
// Quản lý Tài khoản người dùng
Route::get('/nguoidung', [NguoiDungController::class, 'getDanhSach'])->name('nguoidung');
Route::get('/nguoidung/them', [NguoiDungController::class, 'getThem'])->name('nguoidung.them'); Route::post('/nguoidung/them', [NguoiDungController::class, 'postThem'])->name('nguoidung.them'); Route::get('/nguoidung/sua/{id}', [NguoiDungController::class, 'getSua'])->name('nguoidung.sua'); Route::post('/nguoidung/sua/{id}', [NguoiDungController::class, 'postSua'])->name('nguoidung.sua'); Route::get('/nguoidung/xoa/{id}', [NguoiDungController::class, 'getXoa'])->name('nguoidung.xoa');