Laravel 缓存实例教程(二) —— 基于模型+缓存对文章增删改查进行优化


memcached

上一节我们在config/cache.php中配置好了缓存驱动,其实我们配置使用什么缓存驱动在应用层面是体会不到的,因为Laravel为不同的缓存驱动提供了统一的接口,针对不同的驱动调用的代码完全一致。至于使用什么驱动则完全根据业务需求和使用习惯来,这里我们使用的是memcached。至于各种驱动之间的区别我们在上一节已经详细阐述过了,这里不再赘述。

本节我们将以文章的增删改查作为实例系统讲述缓存的使用,这个实例是对之前创建RESTFul风格控制器实现文章增删改查这篇教程的改造和升级,我们将在其基础上融合进Eloquent ORM和模型事件,将应用的场景直接拉到生成环境。

1、准备工作

路由及控制器

路由的定义和控制器的创建保持和创建RESTFul风格控制器实现文章增删改查中一样。

创建数据表

关于文章对应数据表我们在数据库部分使用查询构建器实现对数据库的高级查询已有提及,这里我们使用之前创建的数据表即可。

创建文章模型

关于文章模型Post的创建也和之前Eloquent ORM部分讲ORM概述、模型定义及基本查询中创建的一致。

2、修改控制器

在之前我们是通过缓存实现对文章的增删改查操作,这里我们将其修改为通过数据库实现增删改查操作:

  1. <?php
  2.  
  3. namespace App\Http\Controllers;
  4.  
  5. use Illuminate\Http\Request;
  6.  
  7. use Cache;
  8. use App\Models\Post;
  9.  
  10. use App\Http\Requests;
  11. use App\Http\Controllers\Controller;
  12.  
  13. class PostController extends Controller
  14. {
  15. /**
  16. * 显示文章列表.
  17. *
  18. * @return Response
  19. */
  20. public function index()
  21. {
  22. //使用all获取所有数据,如果数据量大采用分页获取
  23. $posts = Post::all();
  24. if(!$posts)
  25. exit('还没有发布任何文章!');
  26.  
  27. $html = '<ul>';
  28.  
  29. foreach ($posts as $post) {
  30. $html .= '<li><a href='.route('post.show',['post'=>$post]).'>'.$post->title.'</li>';
  31. }
  32.  
  33. $html .= '</ul>';
  34.  
  35. return $html;
  36. }
  37.  
  38. /**
  39. * 创建新文章表单页面
  40. *
  41. * @return Response
  42. */
  43. public function create()
  44. {
  45. $postUrl = route('post.store');
  46. $csrf_field = csrf_field();
  47. $html = <<<CREATE
  48. <form action="$postUrl" method="POST">
  49. $csrf_field
  50. <input type="text" name="title"><br/><br/>
  51. <textarea name="content" cols="50" rows="5"></textarea><br/><br/>
  52. <input type="submit" value="提交"/>
  53. </form>
  54. CREATE;
  55. return $html;
  56. }
  57.  
  58. /**
  59. * 将新创建的文章存储到存储器
  60. *
  61. * @param Request $request
  62. * @return Response
  63. */
  64. public function store(Request $request)
  65. {
  66. $title = $request->input('title');
  67. $content = $request->input('content');
  68.  
  69. $post = new Post;
  70. $post->title = $title;
  71. $post->content = $content;
  72. $post->save();
  73.  
  74. return redirect()->route('post.show',['post'=>$post]);
  75. }
  76.  
  77. /**
  78. * 显示指定文章
  79. *
  80. * @param int $id
  81. * @return Response
  82. */
  83. public function show($id)
  84. {
  85.  
  86. $post = Cache::get('post_'.$id);
  87. if(!$post){
  88. $post = Post::find($id);
  89. if(!$post)
  90. exit('指定文章不存在!');
  91. Cache::put('post_'.$id,$post,60*24*7);
  92. }
  93.  
  94. if(!Cache::get('post_views_'.$id))
  95. Cache::forever('post_views_'.$id,0);
  96. $views = Cache::increment('post_views_'.$id);
  97. Cache::forever('post_views_'.$id,$views);
  98.  
  99. $editUrl = route('post.edit',['post'=>$post]);
  100. $deleteUrl = route('post.destroy',['post'=>$post]);
  101. $html = <<<POST
  102. <h3>{$post->title}</h3>
  103. <p>{$post->content}</p>
  104. <i>已有{$views}人阅读</i>
  105. <p>
  106. <a href="{$editUrl}">编辑</a>
  107. </p>
  108. POST;
  109.  
  110. return $html;
  111. }
  112.  
  113. /**
  114. * 显示编辑指定文章的表单页面
  115. *
  116. * @param int $id
  117. * @return Response
  118. */
  119. public function edit($id)
  120. {
  121. $post = Post::find($id);
  122.  
  123. if(!$post)
  124. exit('指定文章不存在!');
  125.  
  126. $postUrl = route('post.update',['post'=>$post]);
  127. $csrf_field = csrf_field();
  128. $html = <<<CREATE
  129. <form action="$postUrl" method="POST">
  130. $csrf_field
  131. <input type="hidden" name="_method" value="PUT"/>
  132. <input type="text" name="title" value="{$post->title}"><br/><br/>
  133. <textarea name="content" cols="50" rows="5">{$post->content}</textarea><br/><br/>
  134. <input type="submit" value="提交"/>
  135. </form>
  136. CREATE;
  137. return $html;
  138.  
  139. }
  140.  
  141. /**
  142. * 在存储器中更新指定文章
  143. *
  144. * @param Request $request
  145. * @param int $id
  146. * @return Response
  147. */
  148. public function update(Request $request, $id)
  149. {
  150. $post = Post::find($id);
  151. if(!$post)
  152. exit('指定文章不存在!');
  153.  
  154. $title = $request->input('title');
  155. $content = $request->input('content');
  156.  
  157. $post->title = $title;
  158. $post->content = $content;
  159.  
  160. $post->save();
  161.  
  162. return redirect()->route('post.show',['post'=>$post]);
  163. }
  164.  
  165. /**
  166. * 从存储器中移除指定文章
  167. *
  168. * @param int $id
  169. * @return Response
  170. */
  171. public function destroy($id)
  172. {
  173. $post = Post::find($id);
  174. if(!$post)
  175. exit('指定被删除文章不存在!');
  176.  
  177. if($post->delete()){
  178. redirect()->route('post.index');
  179. }else{
  180. exit('删除文章失败!');
  181. }
  182. }
  183. }

需要注意的是在show方法中,我们首先从缓存中取文章数据,缓存中不存在才会去数据库取,同时将数据回写到缓存中,由于对数据库的操作大部分都是读操作,所以这一点小小的改进对性能却有很大提升,尤其是在海量数据时。此外我们还将访问量持久化到缓存中以提升性能。

3、在模型事件中使用缓存

我们还可以通过模型事件在文章进行增删改的时候触发相应事件将修改保存到缓存中,这里我们简单讲模型事件注册到AppServiceProviderboot方法中:

  1. //保存之后更新缓存数据
  2. Post::saved(function($post){
  3. $cacheKey = 'post_'.$post->id;
  4. $cacheData = Cache::get($cacheKey);
  5. if(!$cacheData){
  6. Cache::add($cacheKey,$post,60*24*7);
  7. }else{
  8. Cache::put($cacheKey,$post,60*24*7);
  9. }
  10. });
  11.  
  12. //删除之后清除缓存数据
  13. Post::deleted(function($post){
  14. $cacheKey = 'post_'.$post->id;
  15. $cacheData = Cache::get($cacheKey);
  16. if($cacheData){
  17. Cache::forget($cacheKey);
  18. }
  19. if(Cache::get('post_views_'.$post->id))
  20. Cache::forget('post_views_'.$post->id);
  21. });

我们将缓存有效期设置为一周。这样在文章创建或更新时会将数据保存到缓存,而删除文章时也会从缓存中移除数据,从而保证被删除后的文章查看详情时也不能浏览。