이벤트 시스템
- 컨트롤러 코드가 간결해진다.
- 이벤트 처리 로직을 유연하게 확장하거나 축소할 수 있다.(여러 개의 이벤트 리스너)
- 사용자에게 빠른 UI 응답을 제공한다.(무겁고 시간이 걸리는 일은 이벤트로 던져 다른 클래스에서 처리)
컨트롤러에 이벤트 던지기
app/Http/Controllers/ArticlesController.php
public function store(\App\Http\Requests\ArticlesRequest $request)
{
$article = \App\User::find(1)->articles()->create($request->all());
if(!$article){
return back()->with('flash_message', '글이 저장되지 않았습니다.')->withInput();
}
dump('이벤트를 던집니다.');
echo "<br>";
event(new \App\Events\ArticleCreated($article));
dump('이벤트를 던졌습니다.');
echo "<br>";
//return redirect(route('articles.index'))->with('flash_message', '작성하신 글이 저장되었습니다.');
}
이벤트 레지스트리 채널 등록
app/Http/Providers/EventServiceProvider.php
public function boot()
{
parent::boot();
\Event::listen(
\App\Events\ArticleCreated::class,
\App\Listeners\ArticlesEventListener::class
);
}
listen() 첫번째 인자는 지정한 이벤트가 발생하면 두번째 인자의 클래스에게 처리를 위임한다라는 뜻이다.
위와 같이 등록한 후에 아래와 같이 입력하면
php artisan event:generate
app/Events/ArticleCreated.php
app/Listeners/ArticlesEventListener.php
2개의 파일이 생성된다.
이벤트 클래스 작성
app/Events/ArticleCreated.php
class ArticleCreated
{
use InteractsWithSockets, SerializesModels;
public $article;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(\App\Article $article)
{
$this->article = $article;
}
/**
* Get the channels the event should broadcast on.
*
* @return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
이벤트 채널은 DTO(Data Transfer Object)다.
다른 클래스가 DTO의 프로퍼티에 접근할 수 있게 public 로 선언하자.
__construct(\App\Article $article) 인자는 모델과 변수인 것 같다.
이벤트 리스너 클래스 작성
app/Listeners/ArticlesEventListener.php
<?php
namespace App\Listeners;
//use App\Events\article.created;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class ArticlesEventListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct(){
//
}
/**
* Handle the event.
*
* @param article.created $event
* @return void
*/
public function handle(\App\Events\ArticleCreated $event)
{
dump('이벤트를 받았습니다. 받은 데이터(상태)는 다음과 같습니다.');
dump($event->article->toArray());
}
}
결과 화면 창
실용적인 이벤트 시스템
포럼 글이 작성되면 관리자에게 메일을 보낸다고 가정하자
컨트롤러에서 기존 이벤트를 던지는 코드는 삭제하고 주석처리했던 return 구문은 살리자.
그리고 새로운 이벤트의 이름으로 작성해주자 ArticleEvent
event(new \App\Events\ArticlesEvent($article));
이벤트 레지스트리 채널 등록
app/Http/Providers/EventServiceProvider.php
\App\Events\ArticlesEvent::class 로 수정해주자
public function boot()
{
parent::boot();
\Event::listen(
\App\Events\ArticlesEvent::class,
\App\Listeners\ArticlesEventListener::class
);
}
이벤트 채널과 리스너 뼈대 코드 한 번에 만들기
php artisan event:generate
이벤트 클래스 뼈대만 만들기
php artisan make:event ArticlesEvent
\App\Listeners\ArticlesEventListener.php 는 이미 있기 때문에 ArticlesEvent 클래스만 생성된다.
이벤트 소스 작성
app/Events/ArticlesEvent.php
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class ArticlesEvent
{
use InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public $article;
public $action;
public function __construct(\App\Article $article, $action = 'created')
{
$this->article = $article;
$this->action = $action;
}
/**
* Get the channels the event should broadcast on.
*
* @return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
이벤트 리스너 소스 작성
app/Listeners/ArticlesEventListener.php
<?php
namespace App\Listeners;
//use App\Events\article.created;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class ArticlesEventListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct(){
//
}
/**
* Handle the event.
*
* @param article.created $event
* @return void
*/
public function handle(\App\Events\ArticlesEvent $event)
{
if($event->action === 'created'){
\Log::info(sprintf(
'새로운 포럼 글이 등록되었습니다.: %s',
$event->article->title
));
}
}
}
포럼에 새 글을 작성해보자
http://localhost:8000/articles/create
storage/logs/laravel.log 에 가보면 마지막줄에 기록이 되었다.
라라벨 내장 이벤트 채널
마이그레이션 및 모델
php artisan make:migration add_last_ligin_column_on_users_table --table=users
database/migrations/TIMESTAMP_addadd_last_ligin_column_on_users_table.php
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->timestamp('last_login')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('last_login');
});
}
php artisan migrate
app\User.php
class User extends Authenticatable
{
protected $dates = ['last_login'];
}
이벤트 리스너
이벤트 레지스트리에 이벤트 채널과 리스너를 동록한다.
사용자가 로그인하면 방출하는 라라벨 내장 이벤트는 Illuminate\Auth\Events\Login
이벤트를 소비할 APP\Listeners\UsersEventListener에 연결하자
protected $listen = [
\Illuminate\Auth\Events\Login::class => [
\APP\Listeners\UsersEventListener::class
],
];
이벤트 채널 및 리스너 뼈대 코드 만들기
php artisan event:generate
app/Http/Listeners/UsersEventListener.php
public function handle(Login $event)
{
$event->user->last_login = \Carbon\Carbon::now();
return $event->user->save();
}
로그인 창을 열어 로그인을 해보자
데이터가 제대로 입력이 되었다.