其实通过之前的步骤我们已经完成了博客的基本功能,这一节也是最后一节,我们来给博客加点料,让博客功能更加完善。
现在这个博客的主要缺憾就是用户不能对文章进行评论,不幸的是,博客评论有很多问题要处理。
首先是稳定、令人满意且通用的评论管理,当然,基于 Laravel 5.1 我们可以添加这个功能到后台管理系统,并且允许用户注册、登录、对文章进行评论等等。创建这些功能都很简单,没什么复杂可言。
但是真正的问题在于垃圾评论。
你将如何防止垃圾评论?使用验证码?黑名单/白名单?还是创建类似 Maksim Surguy 这样的 SPAM Honeypot ?或者通过集成 Akismet ?
坦白说,我不想处理这些令人头疼的事情,这里我们还可以使用第三方评论系统分分钟搞定博客评论。
我们使用 Disqus 实现博客评论。
到 Disqus.com 注册一个免费账号,有了账号并登录之后,点击“Add Disqus to site”链接,会跳转到如下这个页面:
填写好上述页面中的表单并提交之后,在跳转页面中选择“Universal Code”,然后页面会跳转到如下页面:
将方式1中的代码拷贝到页面中评论显示区域即可。
接下来我们在控制器 BlogController
的 showPost()
方法中传递 $slug
变量到显示文章详情的视图页面:
// 将如下这行代码
return view($post->layout, compact('post', 'tag'));
//修改成
return view($post->layout, compact('post', 'tag', 'slug'));
很简单吧!现在文章视图中有了一个额外的可用变量。
在 resources/views/blog/partials
目录下创建一个新的局部视图文件 disqus.blade.php
,编辑该文件内容如下:
<div id="disqus_thread"></div>
<script>
var disqus_config = function () {
this.page.url = 'http://blog.app/blog/{{ $slug }}';
this.page.identifier = 'blog-{{ $slug }}';
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = '//laravel-academy.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>
Please enable JavaScript to view the
<a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a>
</noscript>
将 Disqus 中的通用评论代码拷贝过来,并取消 disqus_config
变量的注释,然后修改 this.page.url
和 this.page.identifier
的值。
我们将传递到视图的 $slug
变量作为 Disqus 的标识符以便于聚合该文章下的所有评论。
最后,更新 resources/views/blog/partials
目录下的 page-footer.blade.php
文件内容如下:
@if(isset($slug) && $slug)
<hr>
<div class="container">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
@include('blog.partials.disqus')
</div>
</div>
@endif
<hr>
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<p class="copyright">Copyright © {{ config('blog.author') }}</p>
</div>
</div>
</div>
</footer>
好了,现在你的博客有评论功能了,在文章详情页底部现在可以看到 Disqus 评论框:
除了评论之外,很多博客还支持分享文章或站点到社交媒体功能,比如微信、微博、QQ、豆瓣、Facebook、Twitter等。要实现这一功能,可以借助百度分享或者 JiaThis 的分享代码。将相应分享代码拷贝到到页面相应位置即可,该操作很简单,这里不做演示。
RSS 订阅对大部分博客应用而言是必备功能。在 Laravrel 5.1 中实现 RSS 订阅非常便捷。
我们使用 suin/php-rss-writer
来生成 RSS 文件。
首先使用 Composer 安装该依赖包:
composer require suin/php-rss-writer
让我们来创建一个服务类用于创建和返回 RSS 订阅。在 app/Services
目录下创建一个 RssFeed.php
文件,并编辑其内容如下:
<?php
namespace App\Services;
use App\Post;
use Carbon\Carbon;
use Illuminate\Support\Facades\Cache;
use Suin\RSSWriter\Channel;
use Suin\RSSWriter\Feed;
use Suin\RSSWriter\Item;
class RssFeed
{
/**
* Return the content of the RSS feed
*/
public function getRSS()
{
if (Cache::has('rss-feed')) {
return Cache::get('rss-feed');
}
$rss = $this->buildRssData();
Cache::add('rss-feed', $rss, 120);
return $rss;
}
/**
* Return a string with the feed data
*
* @return string
*/
protected function buildRssData()
{
$now = Carbon::now();
$feed = new Feed();
$channel = new Channel();
$channel
->title(config('blog.title'))
->description(config('blog.description'))
->url(url())
->language('en')
->copyright('Copyright (c) '.config('blog.author'))
->lastBuildDate($now->timestamp)
->appendTo($feed);
$posts = Post::where('published_at', '<=', $now)
->where('is_draft', 0)
->orderBy('published_at', 'desc')
->take(config('blog.rss_size'))
->get();
foreach ($posts as $post) {
$item = new Item();
$item
->title($post->title)
->description($post->subtitle)
->url($post->url())
->pubDate($post->published_at->timestamp)
->guid($post->url(), true)
->appendTo($channel);
}
$feed = (string)$feed;
// Replace a couple items to make the feed more compliant
$feed = str_replace(
'<rss version="2.0">',
'<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">',
$feed
);
$feed = str_replace(
'<channel>',
'<channel>'."\n".' <atom:link href="'.url('/rss').
'" rel="self" type="application/rss+xml" />',
$feed
);
return $feed;
}
}
添加 rss_size
配置项到配置文件 config/blog.php
,我们会在 RSSFeed
服务类中用这个配置值判断在 RSS 显示多少篇文章:
<?php
return [
'name' => "Laravel 学院",
'title' => "Laravel 学院",
'subtitle' => 'http://laravelacademy.org',
'description' => 'Laravel学院致力于提供优质Laravel中文学习资源',
'author' => '学院君',
'page_image' => 'home-bg.jpg',
'posts_per_page' => 10,
'rss_size' => 25,
'uploads' => [
'storage' => 'local',
'webpath' => '/uploads/',
],
'contact_email'=>env('MAIL_FROM'),
];
要实现 RSS 订阅功能还有三件事情要做。首先是添加路由到 app/Http/routes.php
:
// 在下面这个路由后面
Route::post('contact', 'ContactController@sendContactInfo');
// 添加新的路由
get('rss', 'BlogController@rss');
接下来更新 blog.layouts.master
视图文件 :
// 将如下这行代码
<title>{{ $title or config('blog.title') }}</title>
// 替换为
<title>{{ $title or config('blog.title') }}</title>
<link rel="alternate" type="application/rss+xml" href="{{ url('rss') }}"
title="RSS Feed {{ config('blog.title') }}">
最后,更新 BlogController
:
// 在控制器顶部添加如下这个use语句
use App\Services\RssFeed;
// 同时在控制器中添加如下这个方法
public function rss(RssFeed $feed)
{
$rss = $feed->getRSS();
return response($rss)
->header('Content-type', 'application/rss+xml');
}
好了,现在去浏览器中访问 http://blog.app/rss
你将会看到想要看到的东西。
如果想要在页面中显示 RSS 订阅链接,编辑 page-footer.blade.php
视图文件内容如下:
@if(isset($slug) && $slug)
<hr>
<div class="container">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
@include('blog.partials.disqus')
</div>
</div>
@endif
<hr>
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<ul class="list-inline text-center">
<li>
<a href="{{ url('rss') }}" data-toggle="tooltip"
title="RSS feed">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-rss fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
</ul>
<p class="copyright">Copyright © {{ config('blog.author') }}</p>
</div>
</div>
</div>
</footer>
这样在博客底部会显示如下图标:
最后,我们为博客生成站点地图以利于SEO。
实现思路和 RSS 订阅一样:
在 app/Services
目录下新建一个 SiteMap.php
,编辑其内容如下:
<?php
namespace App\Services;
use App\Post;
use Carbon\Carbon;
use Illuminate\Support\Facades\Cache;
class SiteMap
{
/**
* Return the content of the Site Map
*/
public function getSiteMap()
{
if (Cache::has('site-map')) {
return Cache::get('site-map');
}
$siteMap = $this->buildSiteMap();
Cache::add('site-map', $siteMap, 120);
return $siteMap;
}
/**
* Build the Site Map
*/
protected function buildSiteMap()
{
$postsInfo = $this->getPostsInfo();
$dates = array_values($postsInfo);
sort($dates);
$lastmod = last($dates);
$url = trim(url(), '/') . '/';
$xml = [];
$xml[] = '<?xml version="1.0" encoding="UTF-8"?'.'>';
$xml[] = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
$xml[] = ' <url>';
$xml[] = " <loc>$url</loc>";
$xml[] = " <lastmod>$lastmod</lastmod>";
$xml[] = ' <changefreq>daily</changefreq>';
$xml[] = ' <priority>0.8</priority>';
$xml[] = ' </url>';
foreach ($postsInfo as $slug => $lastmod) {
$xml[] = ' <url>';
$xml[] = " <loc>{$url}blog/$slug</loc>";
$xml[] = " <lastmod>$lastmod</lastmod>";
$xml[] = " </url>";
}
$xml[] = '</urlset>';
return join("\n", $xml);
}
/**
* Return all the posts as $url => $date
*/
protected function getPostsInfo()
{
return Post::where('published_at', '<=', Carbon::now())
->where('is_draft', 0)
->orderBy('published_at', 'desc')
->lists('updated_at', 'slug')
->all();
}
}
首先编辑路由文件 routes.php
:
// 在如下这行之后
get('rss', 'BlogController@rss');
// 添加新的路由
get('sitemap.xml', 'BlogController@siteMap');
然后编辑控制器 BlogController
:
// 在控制器顶部添加如下use语句
use App\Services\SiteMap;
// 同时在控制器中新增这个方法
public function siteMap(SiteMap $siteMap)
{
$map = $siteMap->getSiteMap();
return response($map)
->header('Content-type', 'text/xml');
}
到浏览器访问 http://blog.app/sitemap.xml
,页面显示如下(以下是部分截图):
至此,我们的博客系列告一段落,你已经构建起一个完整的博客应用,并且在此过程中相信你也学到了很多 Laravel 技能,但愿这个项目能作为大家学习 Laravel 的入门项目,并在此基础上,初步掌握 Laravel 框架,也希望大家在以后的进阶之路上越走越远,越走越好!