# 文章详情页
## 分析
* 列表页和详情页只有两个页面公用了一部分的内容 可以使用 if 判断的方式 使用同一个视图和控制器
* 左侧菜单的逻辑和页眉的逻辑差不多 代码可以共用 可以写到模型里面
* 列表页的分页可以使用`laravel`框架自带的`bootstrap`分页
* 观察发现 原网站详情页 单页面(一级分类下的网站)不会显示原标题 也不会显示发布时间 这里我们统一处理为 单页面还是显示原标题 不显示时间
## 路由
位置 `routes/web.php`
~~~php
Route::get('/categories/{article_category}.html', [ArticlesController::class, 'category'])
->name('articles.category');
Route::get('/articles/{article}.html', [ArticlesController::class, 'show'])
->name('articles.show');
~~~
## 控制器
位置 `app/Http/Controllers/ArticlesController.php`
~~~php
public function show()
{
return view('index.articles.show');
}
public function category()
{
return view('index.articles.show');
}
~~~
### 填充数据
~~~php
public function parent()
{
return $this->belongsTo(self::class, 'parent_id', 'id');
}
//获取顶级ID
public function getTopParentId()
{
$category = self::pluck('parent_id', 'id');
$id = $this->id;
while ($category[$id]) {
$id = $category[$id];
}
return $id;
}
public function getNav()
{
$top_category_id = $this->getTopParentId();
$articles_data = self::find($top_category_id)->articles;
$categories_data = self::find($top_category_id)->children;
return $this->handle_category_data($articles_data, $categories_data);
}
public function handle_category_data($articles_data, $categories_data)
{
$articles = [];
$categories = [];
foreach ($articles_data as $k => $article) {
$articles[$k]['title'] = $article->title;
$articles[$k]['sort'] = $article->sort;
$articles[$k]['url'] = route('articles.show', ['article' => $article->id]);
}
foreach ($categories_data as $k => $category) {
$categories[$k]['title'] = $category->title;
$categories[$k]['sort'] = $category->order;
$categories[$k]['url'] = route('articles.category', ['article_category' => $category->id]);
}
$item = array_merge($articles, $categories);
array_multisort(array_column($item, 'sort'), SORT_ASC, $item);
return $item;
}
~~~
> 这里使用了`while`循环来获取最上级 `$category`数据类似 `[1 => 0,...,5 => 1,...,13 => 5,]`
修改 `app/Http/ViewComposers/ArticleCategoryComposer.php`
~~~php
public function compose(View $view)
{
$nav = ArticleCategory::where('parent_id', 0)
->orderBy('order', 'desc')
->get();
//将文章和分类合并 并排序
if ($nav->isNotEmpty()) {
$articleCategoryModel = new ArticleCategory();
foreach ($nav as $article_category) {
$article_category->son = $articleCategoryModel->handle_category_data($article_category->articles, $article_category->children);
$articles_footer = $article_category->articles()
->where('is_footer', 1)
->get();
$article_category->son_footer = $articleCategoryModel->handle_category_data($articles_footer, $article_category->children);
}
}
$view->with('nav', $nav);
}
~~~
> 处理导航数据的方法 做成了公共方法
继续修改 `app/Http/Controllers/ArticlesController.php`
~~~php
public function show(Article $article)
{
//左侧导航
$nav = $article->category->getNav();
//面包屑
$mbx['level_1']['url'] = $nav[0]['url'];
if ($article->category->parent_id > 0) {
//二级分类下的文章
$mbx['level_1']['title'] = $article->category->parent->title;
$mbx['level_2'] = ['title' => $article->category->title, 'url' => route('articles.category', ['article_category' => $article->category->id])];
} else {
//一级分类下的文章
$mbx['level_1']['title'] = $article->category->title;
$mbx['level_2'] = ['title' => $article->title, 'url' => route('articles.show', ['article' => $article->id])];
}
return view('index.articles.show', ['nav' => $nav, 'mbx' => $mbx, 'row' => $article]);
}
public function category(ArticleCategory $articleCategory)
{
//左侧导航
$nav = $articleCategory->getNav();
//面包屑
$mbx['level_1']['url'] = $nav[0]['url'];
//二级分类下的文章
$mbx['level_1']['title'] = $articleCategory->parent->title;
$mbx['level_2'] = ['title' => $articleCategory->title, 'url' => route('articles.category', ['article_category' => $articleCategory->id])];
//数据分页
$articles = Article::where('category_id', $articleCategory->id)
->orderBy('created_at', 'desc')
->paginate(15);
return view('index.articles.show', ['list' => $articles, 'nav' => $nav, 'mbx' => $mbx, 'row' => $articleCategory]);
}
~~~
> 面包屑的数据 是根据实际情况 组装的代码 首页(固定)》 一级分类(链接为下一级的第一项)》 二级分类、文章详情
视图 `resources/views/index/articles/show.blade.php`
~~~php
@extends('/index/layouts.app')
@section('title',$row->title)
@section('keywords', $row->keywords)
@section('description', $row->abstract)
@section('css')
@stop
@section('content')
<div class="page" style="padding:30px 0;">
<div class="narclass">
<ul>
@foreach($nav as $category)
<li class="cur">
<a href="{{ $category['url'] }}" title="{{ $category['title'] }}">{{ $category['title'] }}</a>
</li>
@endforeach
</ul>
</div>
<div class="right">
<div class="posit">当前位置:首页 »
<a href="{{ $mbx['level_1']['url'] }}"
title="{{ $mbx['level_1']['title'] }}">{{ $mbx['level_1']['title'] }}</a> »
<a href="{{ $mbx['level_2']['url'] }}"
title="{{ $mbx['level_1']['title'] }}">{{ $mbx['level_2']['title'] }}</a>
</div>
@if(request()->route()->named('articles.show'))
<div class="newXq">
<dt class="Xqtitle">{{ $row->title }}</dt>
@if ($row->category->parent_id > 0)
<dd class="Xqdate">发布于:{{ $row->created_at->format('Y-m-d') }}</dd>
@endif
{!! $row->content !!}
</div>
@else
<div class="pnews">
<dl>
@foreach($list as $article)
<dt class="ptitleB"><a
href="{{ route('articles.show',['article'=>$article->id]) }}">{{ $article->title }}</a>
</dt>
<dd class="pdate">发布于:{{ $article->created_at->format('Y-m-d') }}</dd>
@endforeach
</dl>
</div>
<div style="float: left;margin-top: 10px">
{{ $list->links() }}
</div>
@endif
</div>
</div>
@stop
@section('js')
@stop
~~~
使用`bootstrap`分页 修改 `app/Providers/AppServiceProvider.php`
~~~php
public function boot()
{
Paginator::useBootstrapFour();
}
~~~
下载 [bootstrap](https://github.com/twbs/bootstrap/releases/download/v5.1.3/bootstrap-5.1.3-dist.zip)
页面引入文件
`resources/views/index/layouts/app.blade.php`
~~~php
<link href="{{ asset('static/css/bootstrap.min.css') }}" rel="stylesheet" type="text/css" />
~~~
此时分页已经显示出来了 但是是英文 我们现在可以下载中文包 [laravel-lang](https://github.com/overtrue/laravel-lang)
安装
`composer require "overtrue/laravel-lang:~6.0"`
这个时候显示的就是中文
回到首页 发现中间样式乱了 是因为添加的 `bootstrap` 导致的 修改
`public/static/css/style.css`
~~~css
.produ dl {
...
box-sizing: content-box;
}
~~~