2016-08-17 補(bǔ)充 Exception 部分改造方案的內(nèi)容
2016-08-13 補(bǔ)充 View 部分改造方案的內(nèi)容
背景
項(xiàng)目包含若干子站點(diǎn),不同站點(diǎn)功能各異燎潮,但共享底層數(shù)據(jù)及邏輯倔丈。為開(kāi)發(fā)及運(yùn)維效率期間憨闰,決定在一個(gè) Laravel 應(yīng)用內(nèi)實(shí)現(xiàn)整套系統(tǒng)。
本文基于 Laravel 5.2需五,主要介紹如何針對(duì)多站點(diǎn)分別進(jìn)行用戶認(rèn)證的改造鹉动,用意是最大限度利用 Laravel 自帶的認(rèn)證系統(tǒng)。不過(guò)默認(rèn)的認(rèn)證都是根據(jù) 『email』和『password』字段進(jìn)行的宏邮。之后有時(shí)間可能再追加自定義字段比如『phone』的改造方案泽示,本文暫不涉及缸血。
具體方案
為清晰起見(jiàn),項(xiàng)目按照不同站點(diǎn)組織成不同模塊械筛。在 Laravel 原有目錄結(jié)構(gòu)基礎(chǔ)內(nèi)捎泻,分別給各個(gè)站點(diǎn)創(chuàng)設(shè)目錄。
laravel 5.2 project
├── app
│ └── Http
│ └── Controllers
│ ├── Site1
│ └── Site2
└── resources
└── views
├── site1
└── site2
本文以 Admin 為例進(jìn)行說(shuō)明埋哟,如需增加其他站點(diǎn)笆豁,進(jìn)行類似改動(dòng)即可。
Structure
執(zhí)行下列命令生成默認(rèn)路由定欧、控制器及視圖渔呵。
php artisan make:auth
將默認(rèn)的控制器和視圖結(jié)構(gòu)分別復(fù)制到子模塊下,并創(chuàng)建相關(guān)模型砍鸠、遷移表扩氢、修改路由、認(rèn)證配置爷辱。
本例中录豺,分別在 app/Http/Controllers 及 resources/views 下新建 Admin 及 admin 目錄,所有該站點(diǎn)相關(guān)的 Controller 及 view 均放置在上述兩目錄下饭弓。
最終涉及到的文件樹(shù)如下双饥。
laravel 5.2 project
├── app
│ ├── Exceptions
│ │ └── Handler.php (變更)
│ ├── Http
│ │ ├── Controllers
│ │ │ └── Admin (新建)
│ │ │ ├── Auth
│ │ │ │ ├── AuthController.php
│ │ │ │ └── PasswordController.php
│ │ │ └── HomeController.php
│ │ └── routes.php (變更)
│ └── Admin.php (新建)
├── config
│ └── auth.php (變更)
├── database
│ └── migrations
│ ├── 2014_10_12_000000_create_admins_table.php (新建)
│ └── 2014_10_12_100000_create_admin_password_resets_table.php (新建)
└── resources
└── views
└── admin (新建)
├── auth
│ ├── emails
│ │ └── password.blade.php
│ ├── login.blade.php
│ ├── passwords
│ │ ├── email.blade.php
│ │ └── reset.blade.php
│ └── register.blade.php
├── errors
│ └── 503.blade.php
├── home.blade.php
├── layouts
│ └── app.blade.php
└── welcome.blade.php
Config
針對(duì) Admin 新建 provider(下面的 admins),使用 Admin 的 Model弟断。
針對(duì) Admin 新建 guard咏花,使用新建的 provider——『admins』。
config/auth.php
'guards' => [
'admins' => [
'driver' => 'session',
'provider' => 'admins',
],
],
'providers' => [
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class, // 使用自定義的 Model(Admin)
],
],
Admin 站點(diǎn)使用 Auth 門面時(shí)均需指定 guard 為 admins阀趴,例如獲得當(dāng)前登錄用戶:Auth::guard('admins')->user()昏翰。
Router
這里的作用當(dāng)然是讓訪問(wèn) Admin 站點(diǎn)的請(qǐng)求被轉(zhuǎn)入該站點(diǎn)的 Controller。
本例中刘急,不同系統(tǒng)是以站點(diǎn)域名來(lái)區(qū)分的棚菊,當(dāng)然也可以用別的方法。
app/Http/routes.php
Route::group(['domain' => 'admin.example.com', 'namespace' => 'Admin'], function () { // 之前將默認(rèn)認(rèn)證相關(guān)類保持結(jié)構(gòu)復(fù)制到了 Admin 下叔汁,此時(shí)只需簡(jiǎn)單指定公共命名空間即可
Route::auth(); // 各種注冊(cè)统求、登錄、找回密碼的默認(rèn)路由
Route::group(['middleware' => ['auth:admins']], function () { // 指定 auth 的 guard 為 新建的 admins
Route::get('/', 'HomeController@index'); // 登錄成功才能訪問(wèn)的部分放在認(rèn)證保護(hù)內(nèi)
});
});
View
我們需要針對(duì)不同的站點(diǎn)使用不同的樣式或者用戶認(rèn)證邏輯据块,所以將原 resources/views 下文件復(fù)制到 resources/views/admin 下码邻,同時(shí)針對(duì)路徑變更修改代碼。
resources/views/vendor 由于框架中若干處硬編碼另假,所以沒(méi)法直接移走冒滩,用到相關(guān)的功能需另想辦法處理。
-
view() 方法
例如 view('auth浪谴、view('home开睡、view('layouts因苹、view('welcome,初始狀態(tài)下篇恒,這種形式的使用場(chǎng)所如下扶檐。
app/Http/Controllers/HomeController.php:27: return view('home');
app/Http/routes.php:15: return view('welcome');
vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/controllers/HomeController.stub:27: return view('home');
vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php:37: return view('auth.login');
vendor/laravel/framework/src/Illuminate/Foundation/Auth/RegistersUsers.php:33: return view('auth.register');
vendor/laravel/framework/src/Illuminate/Foundation/Auth/ResetsPasswords.php:49: return view('auth.passwords.email');
vendor/laravel/framework/src/Illuminate/Foundation/Auth/ResetsPasswords.php:52: return view('auth.password');
vendor/laravel/framework/src/Illuminate/Foundation/Auth/ResetsPasswords.php:194: return view('auth.passwords.reset')->with(compact('token', 'email'));
vendor/laravel/framework/src/Illuminate/Foundation/Auth/ResetsPasswords.php:197: return view('auth.reset')->with(compact('token', 'email'));
app 目錄下的 view 直接加上 admin. 的前綴就好。
vendor 目錄下的都是通過(guò) trait 的形式被 App\Http\Controllers\Auth\AuthController 使用的胁艰,而且都留好了自定義屬性可供改寫(xiě)款筑,例如下方先判斷若不存在 $this->loginView,才使用默認(rèn)的 auth.login腾么。所以我們只需要繼承默認(rèn)的 AuthController 并指定這些相關(guān)屬性即可奈梳。(參見(jiàn) Controller 部分介紹)
vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
public function showLoginForm()
{
$view = property_exists($this, 'loginView')
? $this->loginView : 'auth.authenticate';
if (view()->exists($view)) {
return view($view);
}
return view('auth.login');
}
-
@include()、@extends() 方法
同樣解虱,還是針對(duì) auth攘须、home、layouts殴泰、welcome 進(jìn)行處理于宙,初始狀態(tài)下 Laravel 只使用到了 layouts.app 這個(gè)文件。
resources/views/auth/login.blade.php:1:@extends('layouts.app')
resources/views/auth/passwords/email.blade.php:1:@extends('layouts.app')
resources/views/auth/passwords/reset.blade.php:1:@extends('layouts.app')
resources/views/auth/register.blade.php:1:@extends('layouts.app')
resources/views/home.blade.php:1:@extends('layouts.app')
resources/views/welcome.blade.php:1:@extends('layouts.app')
vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/auth/login.stub:1:@extends('layouts.app')
vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/auth/passwords/email.stub:1:@extends('layouts.app')
vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/auth/passwords/reset.stub:1:@extends('layouts.app')
vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/auth/register.stub:1:@extends('layouts.app')
vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/home.stub:1:@extends('layouts.app')
vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/welcome.stub:1:@extends('layouts.app')
resources 目錄下的只需要簡(jiǎn)單加上前綴 admin. 即可悍汛。
vendor 目錄下的文件在運(yùn)行時(shí)沒(méi)有作用捞魁,所以不用處理。
-
Auth 門面
由于 Admin 站點(diǎn)新建了 guard离咐,所以使用默認(rèn) Auth 門面的場(chǎng)合也需要更改谱俭。
resources/views/layouts/app.blade.php:56: @if (Auth::guest())
resources/views/layouts/app.blade.php:62: {{ Auth::user()->name }} <span class="caret"></span>
vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/layouts/app.stub:56: @if (Auth::guest())
vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/layouts/app.stub:62: {{ Auth::user()->name }} <span class="caret"></span>
resources 目錄下,用 Admin::guard('admins')-> 替換 Auth:: 來(lái)指定 guard宵蛀。
vendor 目錄下的文件在運(yùn)行時(shí)沒(méi)有作用旺上,所以不用處理。
Migration & Model
畢竟是新增的站點(diǎn)糖埋,參考自帶的 users/password_resets 新建相關(guān)的 table 及 model 即可。
Controller
將原 app/Http/Controllers 下相關(guān)結(jié)構(gòu)復(fù)制到 Admin 下后窃这,修改 namespace瞳别,然后針對(duì) Admin 相關(guān)進(jìn)行變動(dòng)。
此處以 AuthController 為例杭攻,PasswordController 類似:
app/Http/Controllers/Admin/Auth/AuthController.php
use App\Http\Controllers\Auth\AuthController as BaseAuthController;
class AuthController extends BaseAuthController // 簡(jiǎn)單起見(jiàn)直接繼承默認(rèn)類
{
/* 這里的屬性都是給 trait 用的祟敛,所以不能指定為 private */
protected $guard = 'admins'; // 指定 config/auth.php 中 guard
protected $loginView = 'admin.auth.login'; // 指定登錄用 view
protected $registerView = 'admin.auth.register'; // 指定注冊(cè)用 view
protected function validator(array $data) // 注冊(cè)時(shí)使用的驗(yàn)證規(guī)則
{
return Validator::make($data, [ // 根據(jù) Admin 所需的信息修改驗(yàn)證配置
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:admins', // 使用 admins 表
'password' => 'required|confirmed|min:6',
'terms' => 'required',
]);
}
protected function create(array $data)
{
return Admin::create([ // 使用自定義的 Model(Admin)
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}
Exception
如果需要自定義 error 頁(yè)面(之前把 resources/views/errors 移到 resources/views/admin/errors)的情況下,由于 Laravel 的異常固定由 App\Exceptions\Handler 處理兆解,所以需要在此類中針對(duì)不同站點(diǎn)分別指定錯(cuò)誤處理 view馆铁。具體來(lái)說(shuō)是修改指定錯(cuò)誤頁(yè)的邏輯,在子類中根據(jù)請(qǐng)求的域名(本例的區(qū)分規(guī)則)指定該站點(diǎn)的錯(cuò)誤頁(yè) view锅睛。
app/Exceptions/Handler.php
class Handler extends ExceptionHandler
{
protected $host;
public function render($request, Exception $e)
{
$this->host = $request->getHost(); // 記錄請(qǐng)求站點(diǎn)的域名
return parent::render($request, $e);
}
protected function renderHttpException(HttpException $e) // 修改并覆蓋父類的方法埠巨,用于指定子站點(diǎn)錯(cuò)誤頁(yè) view
{
switch($this->host)
{
case 'admin.example.com': // 針對(duì) Admin 站點(diǎn)指定路徑中的 admin
$prefix = 'admin';
break;
}
$prefix = empty($prefix) ? '' : $prefix . '.';
$status = $e->getStatusCode();
$errorView = $prefix . "errors.{$status}"; // 指定錯(cuò)誤頁(yè) view
if (view()->exists($errorView)) {
return response()->view($errorView, ['exception' => $e], $status, $e->getHeaders());
} else {
return $this->convertExceptionToResponse($e);
}
}
}
參考
整個(gè)改造過(guò)程中历谍,主要參考了如下資料來(lái)源,感謝各位作者的同時(shí)也一并放出參考辣垒。