2016年8月31日 星期三

Laravel 5 Event 事件

Laravel 5 事件綁定器
官方文件名稱為 Laravel Event
此篇文章主要是以實例的方式來描述在什麼樣的情況下會用到 Laravel Event.
舉例來說
想像我們現在有一個需求是要記錄使用者在登入登出時的 IP 及最後登入時間並寫入 logs
下列有幾種方法,在這篇文章中午會介紹各種用法:
  1. 在 中介層(Middleware) 時處理 – 在 route 時綁定 指定的 middleware.
  2. 在 事件(Event) 時處理 – 根據 Laravel 官方所提供的預設事件 
  3. 在 控制器(Controller)時處理 – request
事件參數
artisan.start$application
auth.attempt$credentials, $remember, $login
auth.login$user, $remember
auth.logout$user
cache.missed$key
cache.hit$key, $value
cache.write$key, $value, $minutes
cache.delete$key
connection.{name}.beginTransaction$connection
connection.{name}.committed$connection
connection.{name}.rollingBack$connection
illuminate.query$query, $bindings, $time, $connectionName
illuminate.queue.after$connection, $job, $data
illuminate.queue.failed$connection, $job, $data
illuminate.queue.stoppingnull
mailer.sending$message
router.matched$route, $request
composing:{view name}$view
creating:{view name}$view
上圖為 Laravel 5 所提供的預設事件

在 中介層(Middleware) 時處理

在 Route 中我們加上登入時的路由

路由設定中介層

app/Http/routes.php
// Login middleware
Route::group(['middleware' => 'login'], function () {
Route::post('login', function () {
// 使用 Login 中介層
});
});
view rawroutes.php hosted with ❤ by GitHub
第 2 行使用 route group 將登入時須先過 login 這個中介層

註冊中介層

這個名稱需定義在app/Http/Kernel.php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* @var array
*/
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
];
/**
* The application's route middleware.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
// 加入一個我們自定義的 login middleware
'login' => \App\Http\Middleware\Login\InsertLogs::class,
];
}
view rawKernel.php hosted with ❤ by GitHub
第 29 行註冊我們自定義的 login middleware

實作中介層

接著我們就要來撰寫中介層的內容 middleware
app/Http/Middleware/InsertLogs.php
namespace App\Http\Middleware\Login;
use Closure;
use Carbon;
use Maras0830\Repository\LogsRepository;
class InsertLogs
{
protected $logsRepository;
/**
* Create the middleware.
* @param LogsRepository $logsRepository
* @return void
*/
public function __construct(LogsRepository $logsRepository)
{
$this->logsRepository = $logsRepository;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request);
// 執行登入後的記錄
if (Auth::check())
$this->logsRepository->insertMemberLoginLogs(Auth::user(), $request->ip(), Carbon::NOW());
return $response;
}
}
view rawInsertLogs.php hosted with ❤ by GitHub
我們在 _construct 時注入我自定義的 repository interface
第 35~36 行為當使用者成功登入時我們填加一筆紀錄,其實這邊又有更多寫法
例如你可以先用 eloquent-relationships 去關聯 logs 並直接新增
在此就不多實作 insertMemberLoginLogs 方法

在 事件(Event) 時處理

第 1 步:註冊事件

註冊 Event listren 在 app/Event.php , 處理事件通常放在 app/Handlers/Events
app/Events/MemberLoginEvent.php
namespace App\Events;
use App\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Http\Request;
class MemberLoginEvent extends Event {
use SerializesModels;
public $request;
/**
* Create a new event instance.
* @param Request $request
* @return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
}
view rawMemberLoginEvent.php hosted with ❤ by GitHub

第 2 步:註冊事件監聽器

app/Providers/EventServiceProvider.php
namespace App\Providers;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
// 註冊自定義的監聽器
'App\Events\MemberLoginEvent' => [
'App\Handlers\Events\MemberLoginEventHandler',
],
];
/**
* Register any other events for your application.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
//
}
}
第 17 行我們註冊監聽自定義的事件綁定到我們接下來會需要處理的 handler
注意:這邊的指定方法跟routes指定的方式一樣,意思是你可以使用 @ 去指定你想要的 handler class 中的方法(例如:EventServiceProvider@doSomethingMethod,EventServiceProvider class 中的 doSomethingMethod),若無指定預設使用 handle method。
若是使用 laravel 所提供的方法
註冊監聽可以改為(此方法的差異在於不用實作第一步:註冊事件 及 在第三步時已經有定義好的欄位 Laravel 5 所提供的預設事件):
namespace App\Providers;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
// 註冊登入監聽器
'auth.login' => [
'App\Handlers\Events\AuthLoginEventHandler',
],
];
/**
* Register any other events for your application.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @return void
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
//
}
}

第 3 步:實作事件

app/Handlers/Events/MemberLoginEventHandler.php
namespace App\Handlers\Events;
use App\Events\MemberLoginEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;
use Maras\Contracts\LogsRepositoryInterface;
class MemberLoginEventHandler {
protected $logsRepository;
/**
* Create the event handler.
* @param LogsRepository $logsRepository
* @return void
*/
public function __construct(LogsRepositoryInterface $logsRepository)
{
$this->logsRepository = $logsRepository;
}
/**
* Handle the event.
*
* @param MemberLoginEvent $event
* @return void
*/
public function handle(MemberLoginEvent $event)
{
// 執行登入後的記錄
if (Auth::check())
$this->logsRepository->insertMemberLoginLogs(Auth::user(), $event->request->ip(), Carbon::NOW());
}
}
第 15 行為當 construct時注入自定義的 repository interface,第 28~29 行,為處理此事件的過程。

若是使用 laravel 所提供的事件方法

namespace App\Handlers\Events;
use App\Events\MemberLoginEvent;
use Maras\Contracts\LogsRepositoryInterface;
use Auth;
use Carbon\Carbon;
class MemberLoginEventHandler
{
protected $logsRepository;
/**
* Create the event handler.
* @param LogsRepository $logsRepository
* @return void
*/
public function __construct(LogsRepositoryInterface $logsRepository)
{
$this->logsRepository = $logsRepository;
}
/**
* Handle the event.
*
* @param MemberLoginEvent $event
* @return void
*/
public function handle(MemberLoginEvent $event)
{
執行登入後的記錄 這裏為了範例起見 先將它註解掉
if (Auth::check())
$this->logsRepository->insertMemberLoginLogs(Auth::user(), $event->request->ip(), Carbon::NOW());
}
}

第 4 步:觸發事件

方法一:使用 event 觸發:
  1. event(new MemberLoginEvent());
方法二:使用 Event facade 觸發:
  1. Event::fire(new MemberLoginEvent());
以上兩種方法其實都是 call 同一個 function
通常會在 controller 實作,但若是使用 laravel 5 的預設使用者登入方法則可以不用觸發事件。

在此附上 Laravel 5 Event Source Code: https://github.com/Maras0830/laravel5-event-tutorial

在 控制器(Controller)時處理

這種方式是在確認使用者登入後於 controller 添加
app\Http\Controllers\AuthController.php
namespace App\Http\Controllers;
use Auth;
use Illuminate\Routing\Controller;
use Maras\Contracts\LogsRepositoryInterface;
class AuthController extends Controller
{
protected $logsRepository;
/**
* Create AuthController.
*
* @param LogsRepository $logsRepository
* @return void
*/
public function __construct(LogsRepositoryInterface $logsRepository)
{
$this->logsRepository = $logsRepository;
}
/**
* Handle an authentication attempt.
*
* @return Response
*/
public function authenticate()
{
if (Auth::attempt(['email' => $email, 'password' => $password]))
{
$this->logsRepository->insertMemberLoginLogs(Auth::user(), $this->request->ip(), Carbon::NOW());
return redirect()->intended('dashboard');
}
}
}
view rawAuthController.php hosted with ❤ by GitHub

reference : http://codingweb.tw/2016/06/04/laravel-5-event-事件/

沒有留言:

wibiya widget