前台會員:members 資料表
後台使用者:users 資料表
參考專案:https://github.com/ronrun/laravel-multiauth/tree/9.x_two-tables
要不要拆資料表?
參考國外文章與留言,會看到有些人說不需要拆,沒意義。要拆也有要拆的理由。
1 我的啟蒙導師 opencart 一直都是拆開的。前台叫 customer, 後台叫 user。
2 後台使用者基本上只要帳號跟密碼。其它都不是必要的。但是可能我們會可能會想知道前台會員的性別、生日、住址、電話,若是購物網站,還需要收貨地址、收貨人姓、收貨人名字、付款人地址、付款人性、付款人名字… 。後台使用者不需要佔用這些欄位。
3 對非資訊專業的人來說,當他們知道是放一起,可能多少都會有一點疑慮。
還有一種分法,users 保留登入登出常用欄位。把不常用的欄位另外拆成一張表。
users
user_profiles
聽說這樣拆的目的是效率會比較好。我是沒有實作過。
總之,本文要講的是如何使用兩張表: users, members。
建立 members 資料表
php artisan make:migration create_members_table
在 database/migrations 資料夾可以找到,檔名開頭是年月日,後面是 _create_members_table.php
把 users 的 up() 那一段複製過來,改成 members
public function up()
{
Schema::create('members', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->boolean('is_active')->default('1');
$table->rememberToken();
$table->timestamps();
});
}
可以稍微修改一下 users 資料表
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name',50);
$table->string('email',100)->unique();
$table->string('username',20);
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->boolean('is_active')->default('1');
$table->rememberToken();
$table->timestamps();
});
}
建立管理者資料
編輯 database\seeders\DatabaseSeeder.php
use App\Models\User;
...
public function run()
{
User::truncate();
User::create([
'username' => 'admin',
'email' => 'admin@example.org',
'password' => bcrypt('123456'),
'name' => 'Administrator',
'is_active' => 1,
]);
}
重建 Migration 並執行 Seederphp artisan migrate:refresh --seed
建立 Member Model
php artisan make:model Member
把 User Model 的內容複製過來,Class 名稱改成 Member, 存檔。
修改 User Model
新增 username 欄位
protected $fillable = [
'name',
'email',
'username',
'password',
];
password_resets 是否要處理?
例如也拆成兩個?應該不需要。目前沒看到網路上有文章是拆開的。
新增 guard
修改 config\auth.php
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
改成
'defaults' => [
'guard' => 'web',
'passwords' => 'members',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
改成
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'members',
],
'admin' => [
'driver' => 'session',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
],
改成
'providers' => [
'members' => [
'driver' => 'eloquent',
'model' => App\Models\Member::class,
],
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
],
前台 LoginController
開頭加 use Auth;
新增 overwrite 函數
use Auth;
/**
* Overwrite
*/
protected function guard()
{
return Auth::guard('web');
}
/**
* Overwrite
*/
protected function authenticated(Request $request, $user)
{
return redirect(route('home'));
}
/**
* Overwrite
*/
public function logout(Request $request)
{
$this->guard()->logout();
if (!Auth::guard('web')->check() && !Auth::guard('admin')->check()){
$request->session()->invalidate();
}
return redirect(route('login'));
}
guard() :指定 guard
authenticated(): 登入動作執行之後的轉址
logout(): 將當前的 guard 登出,並且判斷如果完全沒有登入,才清掉 session 。
後台 LoginController
複製前台 LoginController
app\Http\Controllers\Admin\Auth\LoginController.php
一樣開頭加 use Auth; 然後加上上面三個函數。
不一樣的字串:
return Auth::guard('admin');
return redirect(route('admin.dashboard'));
return redirect(route('admin.login'));
再加兩個函數
protected function username()
{
return redirect(route('home'));
}
public function showLoginForm()
{
return view('admin.login');
}
因為 users 資料表用的帳號是 username 而不是 email 。模版位置也不一樣。
app\Exceptions\Handler.php
新增一個函數
protected function unauthenticated($request, AuthenticationException $exception)
{
$guard = \Arr::get($exception->guards(), 0);
switch ($guard) {
case 'admin':
return redirect()->guest(route('admin.login'));
break;
default:
return redirect()->guest(route('login'));
break;
}
}
這個函數原本在 vendor\laravel\framework\src\Illuminate\Foundation\Exceptions\Handler.php
內容是
protected function unauthenticated($request, AuthenticationException $exception)
{
return $request->expectsJson()
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest(route('login'));
}
修改 .env
新增後台資料夾名稱
FOLDER_ADMIN=admin
有了這個參數之後,後台可以另外命名。例如叫 backgarden 後花園,還是挪威的森林之類的。比較不會被外人亂試。
登入後的轉址
app\Http\Middleware\RedirectIfAuthenticated.php
public function handle(Request $request, Closure $next, ...$guards)
{
$backend = env('FOLDER_ADMIN');
//default backend segment is 1, if locale exists,2
if($request->segment(1) == $backend || $request->segment(2) == $backend){
if(Auth::guard('admin')->check()){
return redirect(route('admin.dashboard'));
}
}
if($request->segment(1) != $backend && $request->segment(2) != $backend){
if(Auth::guard('web')->check()){
return redirect(route('home'));
}
}
return $next($request);
}
修改 app\Providers\RouteServiceProvider.php
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
新增一段,使用 admin.php
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/admin.php'));
}
這裡面的 web 不用改。
新增路由 routes/admin.php
<?php
use Illuminate\Support\Facades\Route;
$backend = env('FOLDER_ADMIN');
Route::group(['prefix' => $backend], function () use($backend) {
Route::redirect('/', $backend.'/dashboard');
Route::get('/login', [App\Sections\Admin\Http\Controllers\Auth\LoginController::class, 'showLoginForm'])->name('admin.login');
Route::post('/login', [App\Sections\Admin\Http\Controllers\Auth\LoginController::class, 'login'])->name('admin.login.post');
Route::post('/logout', [App\Sections\Admin\Http\Controllers\Auth\LoginController::class, 'logout'])->name('admin.logout');
Route::group(['middleware' => 'auth:admin'], function () {
Route::get('/dashboard', [App\Sections\Admin\Http\Controllers\DashboardController::class, 'index'])->name('admin.dashboard');
// other admin routes...
});
});
?>
新增 resources\views\admin_layouts\app.blade.php
複製 resources\views\layouts\app.blade.php
@if (Route::has('login'))
改成
@if (Route::has('admin.login'))
route('login')
改成
route('admin.login')
route('logout')
改成
route('admin.logout')
其它檔像 register, forget password... 等等,不會用到,不用處理。
新增 resources\views\admin\login.blade.php
複製 resources\views\auth\login.blade.php
修改開頭 @extends('admin._layouts.app')
符合大小寫的 email 全部改成 username
Eamil Address 改成 Username
新增 resources\views\admin\dashboard.blade.php
複製 resources\views\home.blade.php
修改開頭 @extends('admin._layouts.app')
新增 DashboardController
複製 HomeController
php artisan make:controller Admin\DashboardController
裡面只需要一個函數
public function index()
{
return view('admin.dashboard');
}