添加评论、RSS 订阅和站点地图功能实现


其实通过之前的步骤我们已经完成了博客的基本功能,这一节也是最后一节,我们来给博客加点料,让博客功能更加完善。

1、评论的问题

现在这个博客的主要缺憾就是用户不能对文章进行评论,不幸的是,博客评论有很多问题要处理。

首先是稳定、令人满意且通用的评论管理,当然,基于 Laravel 5.1 我们可以添加这个功能到后台管理系统,并且允许用户注册、登录、对文章进行评论等等。创建这些功能都很简单,没什么复杂可言。

但是真正的问题在于垃圾评论。

你将如何防止垃圾评论?使用验证码?黑名单/白名单?还是创建类似 Maksim Surguy 这样的 SPAM Honeypot ?或者通过集成 Akismet ?

坦白说,我不想处理这些令人头疼的事情,这里我们还可以使用第三方评论系统分分钟搞定博客评论。

2、添加 Disqus 评论框

我们使用 Disqus 实现博客评论。

注册 Disqus 账户并获取评论代码

到 Disqus.com 注册一个免费账号,有了账号并登录之后,点击“Add Disqus to site”链接,会跳转到如下这个页面:

添加Disqus到Laravel学院

填写好上述页面中的表单并提交之后,在跳转页面中选择“Universal Code”,然后页面会跳转到如下页面:

Disqus评论通用代码

将方式1中的代码拷贝到页面中评论显示区域即可。

传递文章 slug 字段到页面

接下来我们在控制器 BlogControllershowPost() 方法中传递 $slug 变量到显示文章详情的视图页面:

  1. // 将如下这行代码
  2. return view($post->layout, compact('post', 'tag'));
  3. //修改成
  4. return view($post->layout, compact('post', 'tag', 'slug'));

很简单吧!现在文章视图中有了一个额外的可用变量。

创建 Disqus 评论局部视图

resources/views/blog/partials 目录下创建一个新的局部视图文件 disqus.blade.php,编辑该文件内容如下:

  1. <div id="disqus_thread"></div>
  2. <script>
  3. var disqus_config = function () {
  4. this.page.url = 'http://blog.app/blog/{{ $slug }}';
  5. this.page.identifier = 'blog-{{ $slug }}';
  6. };
  7. (function() { // DON'T EDIT BELOW THIS LINE
  8. var d = document, s = d.createElement('script');
  9. s.src = '//laravel-academy.disqus.com/embed.js';
  10. s.setAttribute('data-timestamp', +new Date());
  11. (d.head || d.body).appendChild(s);
  12. })();
  13. </script>
  14. <noscript>
  15. Please enable JavaScript to view the
  16. <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a>
  17. </noscript>

将 Disqus 中的通用评论代码拷贝过来,并取消 disqus_config 变量的注释,然后修改 this.page.url 和 this.page.identifier 的值。

我们将传递到视图的 $slug 变量作为 Disqus 的标识符以便于聚合该文章下的所有评论。

更新底部视图文件

最后,更新 resources/views/blog/partials 目录下的 page-footer.blade.php 文件内容如下:

  1. @if(isset($slug) && $slug)
  2. <hr>
  3. <div class="container">
  4. <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
  5. @include('blog.partials.disqus')
  6. </div>
  7. </div>
  8. @endif
  9. <hr>
  10. <footer>
  11. <div class="container">
  12. <div class="row">
  13. <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
  14. <p class="copyright">Copyright © {{ config('blog.author') }}</p>
  15. </div>
  16. </div>
  17. </div>
  18. </footer>

好了,现在你的博客有评论功能了,在文章详情页底部现在可以看到 Disqus 评论框:

Laravel博客Disqus评论框

3、添加分享链接

除了评论之外,很多博客还支持分享文章或站点到社交媒体功能,比如微信、微博、QQ、豆瓣、Facebook、Twitter等。要实现这一功能,可以借助百度分享或者 JiaThis 的分享代码。将相应分享代码拷贝到到页面相应位置即可,该操作很简单,这里不做演示。

4、实现 RSS 订阅

RSS 订阅对大部分博客应用而言是必备功能。在 Laravrel 5.1 中实现 RSS 订阅非常便捷。

安装 Composer 依赖包

我们使用 suin/php-rss-writer 来生成 RSS 文件。

首先使用 Composer 安装该依赖包:

  1. composer require suin/php-rss-writer

创建 RSS 订阅服务

让我们来创建一个服务类用于创建和返回 RSS 订阅。在 app/Services 目录下创建一个 RssFeed.php 文件,并编辑其内容如下:

  1. <?php
  2. namespace App\Services;
  3. use App\Post;
  4. use Carbon\Carbon;
  5. use Illuminate\Support\Facades\Cache;
  6. use Suin\RSSWriter\Channel;
  7. use Suin\RSSWriter\Feed;
  8. use Suin\RSSWriter\Item;
  9. class RssFeed
  10. {
  11. /**
  12. * Return the content of the RSS feed
  13. */
  14. public function getRSS()
  15. {
  16. if (Cache::has('rss-feed')) {
  17. return Cache::get('rss-feed');
  18. }
  19. $rss = $this->buildRssData();
  20. Cache::add('rss-feed', $rss, 120);
  21. return $rss;
  22. }
  23. /**
  24. * Return a string with the feed data
  25. *
  26. * @return string
  27. */
  28. protected function buildRssData()
  29. {
  30. $now = Carbon::now();
  31. $feed = new Feed();
  32. $channel = new Channel();
  33. $channel
  34. ->title(config('blog.title'))
  35. ->description(config('blog.description'))
  36. ->url(url())
  37. ->language('en')
  38. ->copyright('Copyright (c) '.config('blog.author'))
  39. ->lastBuildDate($now->timestamp)
  40. ->appendTo($feed);
  41. $posts = Post::where('published_at', '<=', $now)
  42. ->where('is_draft', 0)
  43. ->orderBy('published_at', 'desc')
  44. ->take(config('blog.rss_size'))
  45. ->get();
  46. foreach ($posts as $post) {
  47. $item = new Item();
  48. $item
  49. ->title($post->title)
  50. ->description($post->subtitle)
  51. ->url($post->url())
  52. ->pubDate($post->published_at->timestamp)
  53. ->guid($post->url(), true)
  54. ->appendTo($channel);
  55. }
  56. $feed = (string)$feed;
  57. // Replace a couple items to make the feed more compliant
  58. $feed = str_replace(
  59. '<rss version="2.0">',
  60. '<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">',
  61. $feed
  62. );
  63. $feed = str_replace(
  64. '<channel>',
  65. '<channel>'."\n".' <atom:link href="'.url('/rss').
  66. '" rel="self" type="application/rss+xml" />',
  67. $feed
  68. );
  69. return $feed;
  70. }
  71. }

更新博客配置

添加 rss_size 配置项到配置文件 config/blog.php,我们会在 RSSFeed 服务类中用这个配置值判断在 RSS 显示多少篇文章:

  1. <?php
  2. return [
  3. 'name' => "Laravel 学院",
  4. 'title' => "Laravel 学院",
  5. 'subtitle' => 'http://laravelacademy.org',
  6. 'description' => 'Laravel学院致力于提供优质Laravel中文学习资源',
  7. 'author' => '学院君',
  8. 'page_image' => 'home-bg.jpg',
  9. 'posts_per_page' => 10,
  10. 'rss_size' => 25,
  11. 'uploads' => [
  12. 'storage' => 'local',
  13. 'webpath' => '/uploads/',
  14. ],
  15. 'contact_email'=>env('MAIL_FROM'),
  16. ];

添加 RSS 路由、链接和方法

要实现 RSS 订阅功能还有三件事情要做。首先是添加路由到 app/Http/routes.php

  1. // 在下面这个路由后面
  2. Route::post('contact', 'ContactController@sendContactInfo');
  3. // 添加新的路由
  4. get('rss', 'BlogController@rss');

接下来更新 blog.layouts.master 视图文件 :

  1. // 将如下这行代码
  2. <title>{{ $title or config('blog.title') }}</title>
  3. // 替换为
  4. <title>{{ $title or config('blog.title') }}</title>
  5. <link rel="alternate" type="application/rss+xml" href="{{ url('rss') }}"
  6. title="RSS Feed {{ config('blog.title') }}">

最后,更新 BlogController

  1. // 在控制器顶部添加如下这个use语句
  2. use App\Services\RssFeed;
  3. // 同时在控制器中添加如下这个方法
  4. public function rss(RssFeed $feed)
  5. {
  6. $rss = $feed->getRSS();
  7. return response($rss)
  8. ->header('Content-type', 'application/rss+xml');
  9. }

好了,现在去浏览器中访问 http://blog.app/rss 你将会看到想要看到的东西。

如果想要在页面中显示 RSS 订阅链接,编辑 page-footer.blade.php 视图文件内容如下:

  1. @if(isset($slug) && $slug)
  2. <hr>
  3. <div class="container">
  4. <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
  5. @include('blog.partials.disqus')
  6. </div>
  7. </div>
  8. @endif
  9. <hr>
  10. <footer>
  11. <div class="container">
  12. <div class="row">
  13. <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
  14. <ul class="list-inline text-center">
  15. <li>
  16. <a href="{{ url('rss') }}" data-toggle="tooltip"
  17. title="RSS feed">
  18. <span class="fa-stack fa-lg">
  19. <i class="fa fa-circle fa-stack-2x"></i>
  20. <i class="fa fa-rss fa-stack-1x fa-inverse"></i>
  21. </span>
  22. </a>
  23. </li>
  24. </ul>
  25. <p class="copyright">Copyright © {{ config('blog.author') }}</p>
  26. </div>
  27. </div>
  28. </div>
  29. </footer>

这样在博客底部会显示如下图标:

Laravel博客RSS订阅图标

5、生成站点地图

最后,我们为博客生成站点地图以利于SEO。

实现思路和 RSS 订阅一样:

创建 SiteMap 服务

app/Services 目录下新建一个 SiteMap.php,编辑其内容如下:

  1. <?php
  2. namespace App\Services;
  3. use App\Post;
  4. use Carbon\Carbon;
  5. use Illuminate\Support\Facades\Cache;
  6. class SiteMap
  7. {
  8. /**
  9. * Return the content of the Site Map
  10. */
  11. public function getSiteMap()
  12. {
  13. if (Cache::has('site-map')) {
  14. return Cache::get('site-map');
  15. }
  16. $siteMap = $this->buildSiteMap();
  17. Cache::add('site-map', $siteMap, 120);
  18. return $siteMap;
  19. }
  20. /**
  21. * Build the Site Map
  22. */
  23. protected function buildSiteMap()
  24. {
  25. $postsInfo = $this->getPostsInfo();
  26. $dates = array_values($postsInfo);
  27. sort($dates);
  28. $lastmod = last($dates);
  29. $url = trim(url(), '/') . '/';
  30. $xml = [];
  31. $xml[] = '<?xml version="1.0" encoding="UTF-8"?'.'>';
  32. $xml[] = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
  33. $xml[] = ' <url>';
  34. $xml[] = " <loc>$url</loc>";
  35. $xml[] = " <lastmod>$lastmod</lastmod>";
  36. $xml[] = ' <changefreq>daily</changefreq>';
  37. $xml[] = ' <priority>0.8</priority>';
  38. $xml[] = ' </url>';
  39. foreach ($postsInfo as $slug => $lastmod) {
  40. $xml[] = ' <url>';
  41. $xml[] = " <loc>{$url}blog/$slug</loc>";
  42. $xml[] = " <lastmod>$lastmod</lastmod>";
  43. $xml[] = " </url>";
  44. }
  45. $xml[] = '</urlset>';
  46. return join("\n", $xml);
  47. }
  48. /**
  49. * Return all the posts as $url => $date
  50. */
  51. protected function getPostsInfo()
  52. {
  53. return Post::where('published_at', '<=', Carbon::now())
  54. ->where('is_draft', 0)
  55. ->orderBy('published_at', 'desc')
  56. ->lists('updated_at', 'slug')
  57. ->all();
  58. }
  59. }

添加路由和控制器方法

首先编辑路由文件 routes.php

  1. // 在如下这行之后
  2. get('rss', 'BlogController@rss');
  3. // 添加新的路由
  4. get('sitemap.xml', 'BlogController@siteMap');

然后编辑控制器 BlogController

  1. // 在控制器顶部添加如下use语句
  2. use App\Services\SiteMap;
  3. // 同时在控制器中新增这个方法
  4. public function siteMap(SiteMap $siteMap)
  5. {
  6. $map = $siteMap->getSiteMap();
  7. return response($map)
  8. ->header('Content-type', 'text/xml');
  9. }

到浏览器访问 http://blog.app/sitemap.xml,页面显示如下(以下是部分截图):

Laravel博客站点地图截图

至此,我们的博客系列告一段落,你已经构建起一个完整的博客应用,并且在此过程中相信你也学到了很多 Laravel 技能,但愿这个项目能作为大家学习 Laravel  的入门项目,并在此基础上,初步掌握 Laravel 框架,也希望大家在以后的进阶之路上越走越远,越走越好!