Laravel 運用事件Event完成新聞訪問量統計

需求描述:用戶瀏覽文章記錄該篇文章的訪問量。

準  備:在文章表中加一個字段‘news_clicks’,用來存放訪問量。

完成效果:同一個用戶session有效時間內瀏覽文章記錄一次訪問量。

這個是關於文章的瀏覽數的實現,當用戶查看文章的時候文章的瀏覽數會增加1,用戶查看文章就是一個事件,有了事件,就需要一個事件監聽器,對監聽的事件發生後執行相應的操作(文章瀏覽數加1),其實這種監聽機制在 Laravel 中是通過觀察者模式實現的.

1、註冊事件以及監聽器
首先我們需要在 app/Providers/目錄下的EventServiceProvider.php中註冊事件監聽器映射關係,如下:

protected $listen = [
        /*'App\Events\Event' => [
            'App\Listeners\EventListener',
        ],*/
        'App\Events\NewsView' => [
            'App\Listeners\NewsViewListener',
        ],
    ];

2、然後項目根目錄下執行如下命令

php artisan event:generate


該命令完成後,會分別自動在 app/Events和app/Listensers目錄下生成 NewsView.php和NewsView

3、定義事件

<?php

namespace App\Events;

use App\http\model\index_news;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class NewsView
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(index_news $news)
    {
        //注入了一個 index_news實例
        $this->news = $news;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        /*return new PrivateChannel('channel-name');*/
        return [];
    }
}

其實看到這些你會發現該事件類只是注入了一個News實例罷了,並沒有包含多餘的邏輯。

4、定義監聽器
事件監聽器在handle方法中接收事件實例,event:generate命令將會自動在handle方法中導入合適的事件類和類型提示事件。在handle方法內,你可以執行任何需要的邏輯以響應事件,我們的代碼實現如下:

<?php

namespace App\Listeners;

use App\Events\NewsView;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Session\Store;

class NewsViewListener
{
    protected $session;
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct(Store $session)
    {
        //
        $this->session = $session;
    }

    /**
     * Handle the event.
     *
     * @param  NewsView  $event
     * @return void
     */
    public function handle(NewsView $event)
    {
        //
        $news = $event->news;
        //先進行判斷是否已經查看過
        if(!$this->hasViewedNews($news)){
            //保存到數據庫
            $news->news_clicks = $news->news_clicks + 1;
            $news->save();
            //看過之後將保存到Session
            $this->storeViewedNews($news);
         }
    }

    /**
     * 判斷當前文章在session中是否已經存在瀏覽記錄
     * @param $news
     * @return bool
     */
    protected function hasViewedNews($news){
        return array_key_exists($news->id,$this->getViewsNews());
    }

    /**
     * 從session中獲取瀏覽記錄
     * @return mixed
     */
    protected function getViewsNews(){
        return $this->session->get('viewed_news',[]);
    }

    /**
     * 存儲瀏覽記錄到session
     * @param $news
     */
    protected function storeViewedNews($news){
        $key = 'viewed_news.'.$news->id;
        $this->session->put($key,time());
    }
}

註釋中也說明了一些邏輯

5、觸發事件
事件和事件監聽完成後,我們要做的就是實現整個監聽,即觸發用戶打開文章事件,觸發事件只需要斷言特定事件被分發,而不需要真正地觸發監聽器,如下:

class DetailController extends BaseController
{
    //資訊詳情
    public function newsDetail($id){
        $news = index_news::find($id);
        $menuId=$news->menu_id;
        switch($menuId){
            case(4):$title='院內新聞';break;
            case(7):$title='通知公告';break;
            case(6):$title='學術動態';break;
            case(32):$title='健康知識';break;
            case(34):$title='人才招聘';break;
        }
        /*觸發瀏覽記錄事件*/
        \event(new NewsView($news));
        return view('home.news.detail',compact('news','title'));
    }

現在打開頁面發現數據庫中的‘news_clicks’已經正常加1了,這樣整個就完成了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章