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了,这样整个就完成了。

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