# 文章详情页 ## 分析 * 列表页和详情页只有两个页面公用了一部分的内容 可以使用 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">当前位置:首页&nbsp;&nbsp;»&nbsp;&nbsp; <a href="{{ $mbx['level_1']['url'] }}" title="{{ $mbx['level_1']['title'] }}">{{ $mbx['level_1']['title'] }}</a>&nbsp;&nbsp;»&nbsp;&nbsp; <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; } ~~~