前面已经完成了博客文章列表的展示页面,现在要来完成博客文章的详情页面。

添加路由

现在添加定义文章详情页的路由,用于展示文章详情。
routes/web.php

  1. <?php
  2. use App\Http\Controllers\ArticlesController;
  3. use App\Http\Controllers\PagesController;
  4. use Illuminate\Support\Facades\Route;
  5. Route::get('/', [PagesController::class, 'root'])->name('root');
  6. Route::get('/about', [PagesController::class, 'about'])->name('about');
  7. Route::get('/contact', [PagesController::class, 'contact'])->name('contact');
  8. Route::get('/blog', [ArticlesController::class, 'index'])->name('articles');
  9. Route::get('/blog/{article}', [ArticlesController::class, 'show'])->name('article.show');

修改控制器

ArticlesController文章控制器中定义 show方法:
app/Http/Controllers/ArticlesController.php

<?php

namespace App\Http\Controllers;

use App\Models\Article;
use Illuminate\Http\Request;

class ArticlesController extends Controller
{
    public function index(){
        $articles = Article::orderBy('created_at', 'desc')->paginate(10);
        return view('articles.index',compact('articles'));
    }

    public function show(Article $article){
        return view('articles.show',compact('article'));
    }
}

Laravel 会自动解析定义在控制器方法(变量名匹配路由片段)中的 Eloquent 模型类型声明。在上面代码中,由于 show() 方法传参时声明了类型 —— Eloquent 模型 Article,对应的变量名 $articles 会匹配路由片段中的 {article},这样,Laravel 会自动注入与请求 URI 中传入的 ID 对应的用户模型实例。

此功能称为 『隐性路由模型绑定』,是『约定优于配置』设计范式的体现,同时满足以下两种情况,此功能即会自动启用:

  1. 路由声明时必须使用 Eloquent 模型的单数小写格式来作为路由片段参数,Article 对应 {article}
Route::get('/blog/{article}', [ArticlesController::class, 'show'])->name('article.show');
  1. 控制器方法传参中必须包含对应的 Eloquent 模型类型声明,并且是有序的:
public function show(Article $article){
    return view('articles.show',compact('article'));
}

当请求 blog.test/blog/1 并且满足以上两个条件时,Laravel 将会自动查找 ID 为 1 的文章并赋值到变量 $article 中,如果数据库中找不到对应的模型实例,会自动生成 HTTP 404 响应。

添加详情页视图

接下来新建一个文章详情页面。
resources/views/articles/show.blade.php

@extends('layouts.app')
@section('title')
    {{$article->title}}
@stop
@section('meta')
    <meta name="description" content="{{$article->excerpt}}" />
@endsection
@section('content')
    <section class="section">
        <div class="container is-max-widescreen">
            <div class="columns">
                <div class="column is-9">
                    <div class="box ">
                        <h1 class="title is-3 mt-2 mb-1 has-text-centered">{{$article->title}}</h1>
                        <div class="has-text-centered">
                            <span class="is-size-7 has-text-grey">发布日期 {{$article->created_at->toDateString()}}</span>
                        </div>
                        <hr class="mt-2 mb-4">
                        <div class="content">
                            {!! $article->content !!}
                        </div>
                    </div>
                </div>
                <div class="column is-3">
                    <div class="box">
                        <p class="has-text-centered">站长:<a href="{{url('/contact')}}" target="_blank">SevDot</a></p>
                        <hr>
                        <div class="is-flex is-justify-content-center">
                            <figure class="image is-96x96">
                                <img class="is-rounded" style="" src="{{asset('images/sevdot_avatar.jpg')}}" alt="SevDot 的头像">
                            </figure>
                        </div>
                        <hr>
                        <div class="has-text-centered">
                            <a href="" class="button is-light"><span class="icon"><i
                                        class="fa fa-github"></i></span></a>
                            <a href="" class="button is-info"><span class="icon"><i class="fa fa-weibo"></i></span></a>
                            <a href="" class="button is-success"><span class="icon"><i class="fa fa-weixin"></i></span></a>
                        </div>
                    </div>
                    <div class="box">
                        <h2 class="title is-5 has-text-centered has-text-success">微信公众号</h2>
                        <hr>
                        <img src="{{asset('images/sevdots.png')}}" alt="">
                    </div>
                </div>
            </div>
        </div>
    </section>
@stop

展示

使用浏览器访问 [http://blog.test/blog/1](http://blog.test/blog) 可以看到文章详情页如下所示:
image.png