說明:本文主要來源於real-time-apps-laravel-5-1-event-broadcastingphp
本文主要基於Laravel的Model Event介紹該框架的實時通訊功能,Laravel模型的生命週期中包含事件:created
、creating
、saved
、saving
、updated
,updating
、deleted
、deleting
、restored
、restoring
,同時結合了Pusher包,有關Pusher的註冊和使用相關信息能夠參考:基於 Pusher 驅動的 Laravel 事件廣播(上)。同時,做者會將開發過程當中的一些截圖和代碼黏上去,提升閱讀效率。備註:Laravel對Model的CRUD操做都會觸發對應的事件,如create操做會在建立前觸發creating事件,建立後觸發created事件,即Model Event。
css
先全局安裝composer:html
curl -sS https://getcomposer.org/installer | php mv composer.phar /usr/local/bin/composer
新建一個空文件夾,在文件夾下,再使用composer安裝Laravel項目:html5
composer create-project laravel/laravel mylaravelapp --prefer-dist
在app/Http/routes.php中寫上資源型路由:mysql
Route::get('/', function () { return view('index'); }); Route::resource('items', 'ItemController', ['except' => ['create', 'edit']]);//排除掉create和edit操做
先建個遷移文件:jquery
php artisan make:migration create_items_table --create=items
在遷移文件database/migrations/*_create_items_table.php中寫上:laravel
/** * Run the migrations. * * @return void */ public function up() { Schema::create('items', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->boolean('isCompleted')->default(false); $table->timestamps(); }); }
新建一個Eloquent Model:ajax
php artisan make:model Item
別忘了配置下數據庫,我用的是MAMP集成環境,數據庫服務是MySQL。數據庫配置主要在config/database.php和.env文件中,在.env文件中寫上對應的host,database,user,password:sql
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_DATABASE=model_event DB_USERNAME=root DB_PASSWORD=model_event
首先在項目根目錄下輸入artisan命令建立個ItemController:數據庫
php artisan make:controller ItemController
在ItemController中寫上增刪改查:
class ItemController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { $uncompletedItems = Item::where('isCompleted', 0)->get(); $completedItems = Item::where('isCompleted', 1)->get(); $data = ['uncompletedItems' => $uncompletedItems, 'completedItems' => $completedItems]; return view('item.index', $data); } /** * Store a newly created resource in storage. * * @return Response */ public function store(Request $request) { $item = new Item; $item->title = $request->title; $item->save(); return response()->json(['id' => $item->id]); } /** * Display the specified resource. * * @param int $id * @return Response */ public function show($id) { $item = Item::find($id); return view('item.show', ['item' => $item]); } /** * Update the specified resource in storage. * * @param int $id * @return Response */ public function update(Request $request, $id) { $item = Item::find($id); $item->isCompleted = (bool) $request->isCompleted; $item->save(); return; } /** * Remove the specified resource from storage. * * @param int $id * @return Response */ public function destroy($id) { $item = Item::find($id); $item->delete(); return; } }
建個reources/views/index.php:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>Todo App</title> <!-- Bootstrap --> {{--<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">--}} <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <!--<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>--> {{--<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>--}} {{--<![endif]-->--}} </head> <body> <div class="container"> <div class="row"> <div class="col-sm-offset-4 col-sm-4"> <h1 class="text-center">Todo App</h1> <form id="addFrm" role="form"> <div class="form-group"> <input type="text" class="form-control" name="title" id="title" required="required" placeholder="Enter title"> </div> <div class="form-group"> <input type="submit" class="btn btn-default" name="submit" value="Add"> </div> </form> <hr> <div id="itemsList"> </div> </div> </div> </div> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <!-- 新 Bootstrap 核心 CSS 文件 --> <!-- jQuery文件。務必在bootstrap.min.js 以前引入 --> <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script> <!-- 最新的 Bootstrap 核心 JavaScript 文件 --> <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> {{--<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>--}} {{--<script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>--}} <!-- Include all compiled plugins (below), or include individual files as needed --> {{--<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>--}} <script> $.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } }); </script> <script> //renders item's new state to the page function addItem(id, isCompleted) {//根據狀態添加item $.get("/items/" + id, function(data) { if (isCompleted) { $("#completedItemsList").append(data); } else { $("#uncompletedItemsList").append(data); } }); } //removes item's old state from the page function removeItem(id) { $('li[data-id="' + id + '"').remove(); } (function($, addItem, removeItem) { $.get( "/items", function( data ) {//DOM加載後,AJAX請求數據,進入ItemController::index() $( "#itemsList" ).html( data ); }); $( "#addFrm" ).submit(function() {//回車或點擊提交按鈕時,AJAX post到ItemController::store()方法,json返回保存的'id'=>$item->id console.log($(this).serialize()); $.post( "/items", $(this).serialize(), function( data ) { addItem(data.id, false); $( "#title" ).val(''); }); return false; }); $(document).on("change", ".isCompleted", function() { var id = $(this).closest('li').data('id'); var isCompleted = $(this).prop("checked") ? 1 : 0;//獲取該item的完成狀態 $.ajax('/items/' + id, {//進入ItemController::update(),更細下item狀態 data: {"isCompleted": isCompleted}, method: 'PATCH', success: function() {//根據狀態變化刪除增長item removeItem(id); addItem(id, isCompleted); } }); }); $(document).on("click", ".deleteItem", function() { var id = $(this).closest('li').data('id'); $.ajax('/items/' + id, {//進入ItemController::destroy()刪除數據庫中item method: 'DELETE', success: function() {//UI刪除該item removeItem(id); } }); }); })(jQuery, addItem, removeItem); </script> </body> </html>
ItemController控制器中返回兩個子視圖item.index、item.show,在resources/views/item中建兩個:
//item.index <legend>未完成的Items</legend> <ul id="uncompletedItemsList" class="list-group"> @foreach ($uncompletedItems as $item) @include('item.show') @endforeach </ul> <hr> <legend>完成的Items</legend> <ul id="completedItemsList" class="list-group"> @foreach ($completedItems as $item) @include('item.show') @endforeach </ul>
//item.show <li class="list-group-item {{ ($item->isCompleted) ? 'text-muted' : '' }}" data-id="{{ $item->id }}"> <span class="badge"> <span class="deleteItem glyphicon glyphicon-remove" aria-hidden="true"></span> </span> <span class="checkbox-inline"> <label> <input type="checkbox" class="isCompleted" value="1" {{ ($item->isCompleted) ? 'checked="checked"' : '' }}> {{ $item->title }} </lable> </span> </li>
一切準備就OK了,個人在MAMP環境輸入路由:http://laravelmodelevent.app:...,新開AB兩個頁面,而後在輸入框裏提交文本後:
A頁面輸入後B頁面只有刷新才能看到最新輸入的文本,不能實時顯示,固然,輸入的文本已經保存在model_event.items表裏了:
頁面裏改變每個item的checkbox後,該item的狀態將會互換,在UI上顯示也是上下位置互換,具體邏輯能夠看views/index.blade.php的JS邏輯,這不是本文的重點,故不詳述。
重點是:在A頁面寫入新文本,B頁面不能實時顯示。這還不是個實時APP。
建立三個廣播事件:
ItemCreated:
當新建一個item完成時觸發
ItemUpdated:
當更新一個item完成時觸發(isCompleted=0或1)
ItemDeleted:
當刪除一個item完成時觸發
在項目根目錄依次輸入:
php artisan make:event ItemCreated php artisan make:event ItemUpdated php artisan make:event ItemDeleted
Laravel事件廣播須要實現ShouldBroadcast接口而且在broadcastOn()方法中寫上廣播頻道:
class ItemCreated extends Event implements ShouldBroadcast { use SerializesModels; public $id; /** * Create a new event instance. * * @return void */ public function __construct(Item $item) { $this->id = $item->id; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return ['itemAction']; } }
class ItemDeleted extends Event implements ShouldBroadcast { use SerializesModels; public $id; /** * Create a new event instance. * * @return void */ public function __construct(Item $item) { $this->id = $item->id; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return ['itemAction']; } }
class ItemUpdated extends Event implements ShouldBroadcast { use SerializesModels; public $id; public $isCompleted; /** * Create a new event instance. * * @return void */ public function __construct(Item $item) { $this->id = $item->id; $this->isCompleted = (bool)$item->isCompleted; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return ['itemAction']; } }
Laravel的Eloquent每一CRUD操做都會觸發Model事件,能夠在service provider裏監聽這些事件從而觸發新建的三個廣播事件,在AppServiceProvider中:
class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { Item::created(function($item){ event(new ItemCreated($item)); }); Item::deleted(function($item){ event(new ItemDeleted($item)); }); Item::updated(function($item){ event(new ItemUpdated($item)); }); } /** * Register any application services. * * @return void */ public function register() { // } }
Pusher的做用、註冊和安裝可參考:
基於 Pusher 驅動的 Laravel 事件廣播(上)
註冊安裝也比較簡單,總之使用Pusher能作個實時APP。
更新resources/views/index.blade.php文件:
... <title>Todo App</title> <!-- Bootstrap --> <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css"> <script src="//js.pusher.com/3.0/pusher.min.js"></script>//引入pusherJS文件 ... $.post( "/items", $(this).serialize(), function( data ) { // addItem(data.id, false);//註銷掉 $( "#title" ).val(''); }); ... $.ajax('/items/' + id, {//進入ItemController::update(),更細下item狀態 data: {"isCompleted": isCompleted}, method: 'PATCH', success: function() {//根據狀態變化刪除增長item // removeItem(id);//註銷掉 // addItem(id, isCompleted);//註銷掉 } }); ... $(document).on("click", ".deleteItem", function() { var id = $(this).closest('li').data('id'); $.ajax('/items/' + id, {//進入ItemController::destroy()刪除數據庫中item method: 'DELETE', success: function() {//UI刪除該item // removeItem(id);//註銷掉 } }); }); })(jQuery, addItem, removeItem); //新加代碼 var pusher = new Pusher("{{env("PUSHER_KEY")}}"); var itemActionChannel = pusher.subscribe('itemAction'); itemActionChannel.bind('App\\Events\\ItemCreated', function (data) { console.log(data.id); addItem(data.id, false); }); itemActionChannel.bind('App\\Events\\ItemDeleted', function (data) { console.log(data.id); removeItem(data.id); }); itemActionChannel.bind('App\\Events\\ItemUpdated', function (data) { removeItem(data.id); addItem(data.id, data.isCompleted); });
新加代碼主要用pusher對象註冊三個事件廣播的頻道'itemAction',並分別綁定三個事件,成功後回調執行對應的UI操做。想要了解更多能夠參考這篇文章:基於 Pusher 驅動的 Laravel 事件廣播(下)
刷新AB頁面,並觀察數據庫model_event.items。
測試實時建立功能。
A頁面輸入文本後發現B頁面不用刷新就實時顯示對應內容,且數據庫已經保存剛剛建立的文本:
測試實時更新功能。
B頁面點擊狀態更新checkbox後,A頁面該item狀態也實時更新,且數據庫isCompleted字段變爲1:
測試實時刪除功能。
A頁面點擊刪除按鈕後,B頁面也實時刪除對應的item,且數據庫該item也刪除:
OK,It is working!!!
總結:本節主要利用Laravel的Model Event來建立一個實時WEB APP,挺好玩的,能夠玩一玩哦。有問題可留言。嘛,過兩天還想結合Laravel的Container Event容器事件新開篇文章,到時見。
歡迎關注Laravel-China。