基于Laravel实现的用户动态模块开发
前言
相信大家都知道,几乎所有的社区应用都有用户动态这个部分,用户可以通过好友动态获能取到更多感兴趣的内容,从而提高社区活跃度和用户粘性。它的实现相对来讲比普通的内容发布要复杂一些,主要体现在内容多样性上。
为了解决这个问题,我们得把这些不同类型的内容抽象,提取共性,使用相同的结构来处理,开发起来就会简单很多。
概念抽象
用户动态,顾名思义,动态的产生,就是一系列事件的历史记录,所以首先关注“事件”这个名词,它有哪些属性:
- 触发者,基于社区所有的事件几乎都是由用户触发的
- 事件主体,事件的主体信息,例如“xxx发布了文章”中的“文章”。
- 事件属性,事件主体不同,所需要的附加信息也不同,比如事件类型。
- 发生时间,记录事件产生的时间,当然了在我们的数据库通常记录了所有数据产生的时间。
我们将用户动态抽象成只有4个基础属性的结构,就比较容易实现了:
-description事件描述 -causer_id或者user_id事件触发者 -subject_id主体ID -subject_type主体类型 -properties事件附加属性 -created_at事件产生时间
而主体部分就是Laravel里的morphrelation,多态关联。
怎么展示
我们的动态展示需求通常有以下几种:
- 我的好友的动态
- 某个人的动态,通常是个人中心
- 全部动态,比如LaravelChina首页的全部动态
- 动态搜索,比较少见
我最近正在开发EasyWeChat新版网站,其中也有用户动态,举例:
xxx发布了讨论《请问大家怎么使用xxx》 xxx评论了xxx的话题《请问大家怎么使用xxx》 xxx回复了xxx的评论“我是按照文档上...” xxx购买了《微信开发:自定义菜单的使用》 xxx关注了xxx ...
你会发现,基本上每种动态的写法都不一样,所以我们还需要记录一个“事件类型”,比如“关注”、“发布”、“回复”、“购买”。
然后我们在blade或者其它模板引擎的使用中,就可以switch...case写法,来应用不同的模板渲染这些样式,比如blade中,我的用法:
@switch($activity->properties['event']??'') @case('discussion.created') ... @break @case('comment.created') ... @break @endswitch
代码实现
前面我们已经讨论完了数据存储以及展示方面的设计,接着就是怎么实现,如果你比较勤劳,可以原生实现,毕竟上面的实现方法已经描述清晰,写点代码实现就搞定了,今天我要推荐的是使用spatie/laravel-activitylog来实现:
安装一直很简单对吧:
$composerinstallspatie/laravel-activitylog-vvv
记录动态
activity()->log('Look,Iloggedsomething');
当然了这种记录没意义,几乎没有任何有用的信息,所以我们通常的用法应该是这样:
activity() ->performedOn($anEloquentModel) ->causedBy($user) ->withProperties(['customProperty'=>'customValue']) ->log('Look,Iloggedsomething'); $lastLoggedActivity=Activity::all()->last(); $lastLoggedActivity->subject;//returnsaninstanceofaneloquentmodel $lastLoggedActivity->causer;//returnsaninstanceofyourusermodel $lastLoggedActivity->getExtraProperty('customProperty');//returns'customValue' $lastLoggedActivity->description;//returns'Look,Iloggedsomething'
方法介绍:
- performedOn($model)设置事件主体,也就是EloquentModel实例
- causedBy($user)设置事件触发者,User实例
- withProperties($properties)上面我们概念里的事件属性
- withProperty($key,$value)事件属性的单个用法
- log($description)事件描述
比如,我们要记录一条,用户发布了讨论:
$discussion=App\Discussion::create([...]); activity()->on($discussion) ->withProperty('event','discussion.created') ->log('发表了话题');
或者用户注册时,我要记录一条动态:
activity()->on($user) ->withProperty('event','user.created') ->log('加入EasyWeChat');
你会发现我都没有设置触发者,因为这个模块如果你没设置触发者默认就是当前登录用户。
展示动态
展示动态就是根据条件从数据库拿出来,这里使用包提供的模型类:Spatie\Activitylog\Models\Activity
useSpatie\Activitylog\Models\Activity;
//全部动态 $activities=Activity::all(); //用户ID为2的动态 $activities=Activity::causedBy(User::find(2))->paginate(15); //以文章ID为13为主体的动态 $activities=Activity::forSubject(Post::find(13))->paginate(15);
接着就是遍历展示就好了。
一些经验与技巧
设置一个专门的动态观察者类来记录动态
$./artisanmake:listenerUserActivitySubscriber
代码如下:
'onUserCreated', 'eloquent.created:App\Discussion'=>'onDiscussionCreated', ]; publicfunctionsubscribe($events) { foreach($this->lisenas$event=>$listener){ $events->lisen($event,__CLASS__.'@'.$listener); } } publicfunctiononUserCreated($user) { activity()->on($user) ->withProperty('event','user.created') ->log('加入EasyWeChat'); } publicfunctiononDiscussionCreated($discussion) { activity()->on($discussion) ->withProperty('event','discussion.created')->log('发表了话题'); } }
然后我们去注册这个订阅类:
在App\Providers\EventServiceProvider中$subscribe中注册这个订阅类:
/** *@vararray */ protected$subscribe=[ \App\Listeners\UserActivitySubscriber::class, ];
上面我们利用了Eloquent模型事件来监听模型的变化,当各种模型事件创建的时候我们调用对应的方法来记录动态,所以实现起来非常的方便。
在事件属性里记录关键信息
看到上面记录动态的时候你可能会问,只存储了ID,这种多态关联,查询的时候会压力很大,比如,我们要将动态显示为:
安小超发布了文章《自定义菜单的使用》
我们如果只是存储了文章的id与类型,我们还需要查询一次文章表,才能得到标题用于显示,这样一个动态列表的话,可能会几十条SQL了,的确是这样的,我的解决方案是这样的:
其实我们的用户动态是不要求100%精准的,所以,我如果在记录时把文章的标题一起存下来是不是就不用再查表了?其实就是,我们在动态列表需要展示的关键信息,比如标题这些一起用withProperties存起来,这样就一条SQL解决了动态列表问题。
这样的做法也有弊端,比如文章改了标题的时候,这里就不同步了,当然你也可以在文章修改时来改这个属性,不过我个人认为没有多大必要。毕竟动态就是记录了当时的情况,后来改标题了并没有什么问题。
OK,用户动态模块的开发就分享到这里,如果你有更高级的实现欢迎随时交流。
关于好友动态部分的实现,根据你的应用量级,以及好友关系的存储各有不同,大家自己集思广益即可,大部分都是先查好友关系再查动态,关联查询也可以,自己实现吧。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。