需求描述:用户浏览文章记录该篇文章的访问量。
准 备:在文章表中加一个字段‘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、定义事件
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方法内,你可以执行任何需要的逻辑以响应事件,我们的代码实现如下:
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了,这样整个就完成了。