Laravel搭建博客 (文章归档)

将文章按照时间顺序归档,进一步了解查询构造器。

数据库语句与查询构造器

使用这些语句能够帮助查询所有不同的年月组合的数据,并且把它们存储为变量:

select year(created_at) year, monthname(created_at) month, count(*) published from posts group by year,month;

将年份存储在year中,月份名字存储在monthname中,并且将年份、月份相同的数量存储在published中,然后按照年份+月份筛选,这样可以得到类似下面的数据:

+------+----------+-----------+
| year | month | published |
+------+----------+-----------+
| 2016 | October | 1 |
| 2017 | December | 1 |
| 2017 | October | 1 |
+------+----------+-----------+

同样的可以在Laravel中来使用这些语句,首先打开tinker测试一下:

>>> App\Post::selectRaw('year(created_at) year, monthname(created_at) month, count(*) published')->groupBy('year','month')->get()
=> Illuminate\Database\Eloquent\Collection {#676
all: [
App\Post {#677
year: 2016,
month: "October",
published: 1,
},
App\Post {#678
year: 2017,
month: "December",
published: 1,
},
App\Post {#679
year: 2017,
month: "October",
published: 1,
},
],
}

这是Laravel中为了方便使用数据库的原始表达式而设置的,具体可以参考Laravel-Raw Methods;还可以在get()后面继续添加toArray()来把数据转化成一个数组。

接下来可以在PostsController的index方法中把查询结果赋值给一个变量,然后传给相应的视图:

public function index(){
$posts = Post::latest()->get();
$archives = Post::selectRaw('year(created_at) year, monthname(created_at) month, count(*) published')
->groupBy('year','month')
->get()
->toArray();
return view('posts.index', compact('posts', 'archives'));
}

之前将侧边栏放在一个单独的视图中:layouts/sidebar.blade.php,现在将Bootstrap原本的模版替换掉:

//原本的模版
<div class="sidebar-module">
<h4>Archives</h4>
<ol class="list-unstyled">
<li><a href="#">March 2014</a></li>
<li><a href="#">February 2014</a></li>
<li><a href="#">January 2014</a></li>
<li><a href="#">December 2013</a></li>
...
</ol>
</div>

//替换为
<div class="sidebar-module">
<h4>Archives</h4>
<ol class="list-unstyled">
@foreach ($archives as $stats)
<li><a href="#">{{$stats['month'].' '.$stats['year']}}</a>
@endforeach
</ol>
</div>

完成之后,主页的侧边栏的Archives效果:

Archives

对文章归档

视图已经完成,但是点击归档链接还无法对文章进行归档,可以使用url参数来对文章归档,修改a标签的herf:

<a href="/?month={{$stats['month']}}&year={{$stats['year']}}"></a>

接下来到PostsController中去更改$posts变量的内容:

//原本$posts变量的获取方法
$posts = Post::latest()->get();

//更改为根据url参数来获取
$posts = Post::latest();
if($month = request('month')){
$posts->whereMonth('created_at', '=', Carbon::parse($month)->month);
}
if($year = request('year')){
$posts->whereYear('created_at', '=', $year);
}
$posts = $posts->get();

这样能够筛选出特定数据的数据集,但是这里还有一个问题,url里传递的月份是以monthname也就是October这样的形式,而筛选的时候使用的whereMonth是数字,所以需要把它们进行一个转换,这里还是使用Carbon这个强大的PHP时间API来完成这项工作,首先引入use Carbon\Carbon,然后把原本的$month换成Carbon::parse($month)->month即可,这一步也可以在tinker中测试,可以查看Laravel——查询构造器

Scope查询与筛选器

这一节内容中文文档中没有,可以参考英文文档:Eloquent:Getting started#Scope query

上面的查询工作还可以使用筛选器,把index方法中获得$posts变量的语句用筛选器来代替:

$posts = Post::latest()
->filter([
'month' => request('month'),
'year' => request('year')
])
->get();

随后在Post的Model中添加一个Scope筛选器方法:

public function scopeFilter($query, $filters){
if($month = $filters['month']){
$query->whereMonth('created_at', '=', Carbon::parse($month)->month);
}
if($year = $filters['year']){
$query->whereYear('created_at', '=', $year);
}
}

然后刷新浏览器,效果与之前一样。

视图组件

把文章归档之后又出现了一个问题,由于是在PostsController的index方法中添加了$archive这个变量,之后在sidebar.blade.php中引用了这个变量,当跳转到其他页面的时候却没有这个$archive变量,但是又会显示sidebar.blade.php的侧边栏,所以就会出现找不到$archive变量的错误,这个时候可以使用Laravel的视图组件功能,来把一些数据组织到同一个地方。

首先,把获取$archive变量的功能提取到Post.php的一个静态函数archives()之中:

static public function archives(){
return static::selectRaw('year(created_at) year, monthname(created_at) month, count(*) published')
->groupBy('year','month')
->orderByRaw('min(created_at) desc')
->get()
->toArray();
}

然后在index函数中去掉原来的$archives变量,到app/Provider/AppServiceProvider.php的boot方法中注册View Composer:

public function boot()
{
view()->composer('layouts.sidebar', function($view){
$view->with('archives', \App\Post::archives());
});
}

$archives变量注册在layouts.sidebar视图中,而无需在index或其他方法中指定$archives变量,现在再次刷新浏览器,每个页面都可以正确的显示Archives栏目。

Gijera · 本站所有文章遵循自由转载-非商用-非衍生-保持署名.
All aritcles of this website follow the CC-3.0 License.知识共享许可协议