1. 安装 generator 代码生成器
代码生成器能让你通过执行一条 Artisan 命令,完成注册路由、新建模型、新建表单验证类、新建资源控制器以及所需视图文件等任务,不仅约束了项目开发的风格,还能极大地提高我们的开发效率。在项目根目录下运行以下命令即可安装
composer require "summerblue/generator:~1.0" --dev
2.生成业务功能骨架
此处我们拟开发一个论坛系统的帖子相关功能,帖子定义为 topics
首先需要设计好 topics 表的字段名称和字段类型:
然后运行php artisan make:scaffold Topic --schema="title:string:index,body:text,user_id:integer:unsigned:index,category_id:integer:unsigned:index,reply_count:integer:unsigned:default(0),view_count:integer:unsigned:default(0),last_reply_user_id:integer:unsigned:default(0),order:integer:unsigned:default(0),excerpt:text:nullable,slug:string:nullable"
即可生成帖子业务功能的骨架,执行结果为:
我们看下代码生成器为我们做了哪些事情:
1.创建帖子的数据库迁移文件 —— 2018_12_23_104258_create_topics_table.php;
2.创建帖子数据工厂文件 —— TopicFactory.php;
3.创建帖子数据填充文件 —— TopicsTableSeeder.php;
4.创建模型基类文件 —— Model.php, 并创建帖子数据模型 Models/Topic;
5.创建帖子控制器 —— TopicsController.php;控制器中自动完成了__construct()
,index()
,show(Topic $topic)
,create()
,store(TopicRequest $request)
,edit(Topic $topic)
,update(TopicRequest $request,Topic $topic)
,destroy(Topic $topic)
方法;
6.创建表单请求的基类文件 —— Request.php,并创建帖子表单请求验证类 TopicRequest.php,用于验证用户的表单输入;
7.创建帖子模型事件监控器 TopicObserver 并在 AppServiceProvider 中注册;
8.创建授权策略基类文件 —— Policy.php,同时创建帖子授权类,并在 AuthServiceProvider 中注册;
9.在 web.php 中更新路由,新增帖子相关的资源路由;
10.新建符合资源控制器要求的三个帖子视图文件,并存放于 resources/views/topics 目录中;
11.执行了数据库迁移命令 artisan migrate
;
因此次操作新建了多个文件,最终执行 composer dump-autoload
来生成 classmap。
3.假数据填充
现在数据表中并没有数据,因此需要填充一些假数据来展示。代码生成器给我们自动生成了 工厂文件、填充文件并自动在 DataSeeder.php 文件中对 填充文件进行了注册。
首先需要填充用户数据,再填充帖子数据,此处讲解如何填充用户数据,填充帖子数据类似。
1.修改工厂文件 UserFactory.php
<?php
use Faker\Generator as Faker;
$factory->define(App\Models\User::class, function (Faker $faker) {
$date_time = $faker->date . ' ' . $faker->time;
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
'introduction' => $faker->sentence(),
'created_at' => $date_time,
'updated_at' => $date_time,
];
});
2 修改填充文件 UserTableSeeder.php
<?php
use Illuminate\Database\Seeder;
use App\Models\User;
class UsersTableSeeder extends Seeder
{
public function run()
{
// 获取 Faker 实例
$faker = app(Faker\Generator::class);
// 头像假数据
$avatars = [
'https://iocaffcdn.phphub.org/uploads/images/201710/14/1/s5ehp11z6s.png',
'https://iocaffcdn.phphub.org/uploads/images/201710/14/1/Lhd1SHqu86.png',
'https://iocaffcdn.phphub.org/uploads/images/201710/14/1/LOnMrqbHJn.png',
'https://iocaffcdn.phphub.org/uploads/images/201710/14/1/xAuDMxteQy.png',
'https://iocaffcdn.phphub.org/uploads/images/201710/14/1/ZqM7iaP4CR.png',
'https://iocaffcdn.phphub.org/uploads/images/201710/14/1/NDnzMutoxX.png',
];
// 生成数据集合
$users = factory(User::class)
->times(10)
->make()
->each(function ($user, $index)
use ($faker, $avatars)
{
// 从头像数组中随机取出一个并赋值
$user->avatar = $faker->randomElement($avatars);
});
// 让隐藏字段可见,并将数据集合转换为数组
$user_array = $users->makeVisible(['password', 'remember_token'])->toArray();
// 插入到数据库中
User::insert($user_array);
// 单独处理第一个用户的数据
$user = User::find(1);
$user->name = 'Summer';
$user->email = '[email protected]';
$user->avatar = 'https://iocaffcdn.phphub.org/uploads/images/201710/14/1/ZqM7iaP4CR.png';
$user->save();
}
}
4.展示帖子列表 index
1.模型关联
开始之前,我们需要对 Topic 数据模型进行修改,新增 category 和 user 的模型关联:
category—— 一个帖子属于一个分类;
user —— 一个帖子拥有一个作者。
更多的模型关联相关知识可参考这篇文章 Laravel 模型关联
2.修改视图文件 index.blade.php,展示数据表中的所有帖子
@extends('layouts.app')
@section('title', '话题列表')
@section('content')
<div class="row mb-5">
<div class="col-lg-9 col-md-9 topic-list">
<div class="card ">
<div class="card-header bg-transparent">
<ul class="nav nav-pills">
<li class="nav-item"><a class="nav-link active" href="#">最后回复</a></li>
<li class="nav-item"><a class="nav-link" href="#">最新发布</a></li>
</ul>
</div>
<div class="card-body">
{{-- 话题列表 --}}
@include('topics._topic_list', ['topics' => $topics])
{{-- 分页 --}}
<div class="mt-5">
{!! $topics->appends(Request::except('page'))->render() !!}
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-3 sidebar">
@include('topics._sidebar')
</div>
</div>
@endsection
5.新建帖子
1.在顶部导航栏新增入口进入新建帖子的页面
2.美化 create_and_edit.blade.php 视图,可能会用到富文本编辑器,可参考这篇文章 Laravel 项目如何引用第三方富文本编辑器 - 已解决
3.验证用户输入的数据,符合验证规则则保存用户填入的数据到 topics 数据表,注意在控制器中的 store 方法中将当前用户的 id 值写入 topics 数据表
6.使用模型监控器
excerpt 字段存储的是话题的摘录,将作为文章页面的 description 元标签使用,有利于 SEO 搜索引擎优化。摘录由文章内容中自动生成,生成的时机是在话题数据存入数据库之前。我们将使用 Eloquent 的 观察器 来实现此功能。
Eloquent 模型会触发许多事件(Event),我们可以对模型的生命周期内多个时间点进行监控: creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored。事件让你每当有特定的模型类在数据库保存或更新时,执行代码。当一个新模型被初次保存将会触发 creating 以及 created 事件。如果一个模型已经存在于数据库且调用了 save 方法,将会触发 updating 和 updated 事件。在这两种情况下都会触发 saving 和 saved 事件。
Eloquent 观察器允许我们对给定模型中进行事件监控,观察者类里的方法名对应 Eloquent 想监听的事件。每种方法接收 model 作为其唯一的参数。代码生成器已经为我们生成了一个观察器文件,并在 AppServiceProvider 中注册。接下来我们要定制此观察器,在 Topic 模型保存时触发的 saving 事件中,对 excerpt 字段进行赋值:
app/Observers/TopicObserver.php
<?php
namespace App\Observers;
use App\Models\Topic;
class TopicObserver
{
public function saving(Topic $topic)
{
$topic->excerpt = make_excerpt($topic->body);
}
}
make_excerpt() 是我们自定义的辅助方法,我们需要在 helpers.php 文件中添加:
app/helpers.php
function make_excerpt($value, $length = 200)
{
$excerpt = trim(preg_replace('/\r\n|\r|\n+/', ' ', strip_tags($value)));
return str_limit($excerpt, $length);
}
7.编辑帖子、删除帖子
编辑功能和删除功能代码生成器已经基本替我们完成。只需要注意一下编辑页面展示的内容是否有其他数据表的内容。
例如该出调用了分类表 categories 中的分类内容,则需要修改 TopicsController 控制器中的 edit 方法为:
public function edit(Topic $topic)
{
$this->authorize('update', $topic);
$categories = Category::all();
return view('topics.create_and_edit', compact('topic', 'categories'));
}
打开模板文件,在分类选择那里加上逻辑判断,有两个地方需要判断,第一个 『请选择分类』那,如果是编辑情况下就去掉 selected ,因为编辑情况下,肯定已经做过分类的选择。第二个是哪个分类被选中的判断,遍历时只要与话题关联的 category_id 一致的话,即可视为选中:
resources/views/topics/create_and_edit.blade.php
<div class="form-group">
<select class="form-control" name="category_id" required>
<option value="" hidden disabled {{ $topic->id ? '' : 'selected' }}>请选择分类</option>
@foreach ($categories as $value)
<option value="{{ $value->id }}" {{ $topic->category_id == $value->id ? 'selected' : '' }}>
{{ $value->name }}
</option>
@endforeach
</select>
</div>
8.权限验证
编辑帖子,删除帖子这两项功能都需要权限验证,不允许普通用户编辑和删除其他人的帖子。
代码生成器已经自动生成了授权策略文件 TopicPolicy.php 并自动在 AuthServiceProvider.php 文件进行了注册,我们将对 update() 方法进行修改,只有当帖子关联作者的 ID 等于当前登录用户 ID 时候才放行:
app/Policies/TopicPolicy.php
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Topic;
class TopicPolicy extends Policy
{
public function update(User $user, Topic $topic)
{
return $topic->user_id == $user->id; //true 允许访问,false 拒绝访问
}
}
然后在 TopicsController 控制器中的 edit() 方法和 update() 方法中加入 $this->authorize('update', $topic);
进行调用即可。
按钮显示
我们也需要对编辑和删除按钮增加显示条件,只有当用户有权限操作时才显示。我们可以很方便地利用 Laravel 授权策略提供的 @can Blade 命令,在 Blade 模板中做授权判断。因为我们的 update 和 destroy 的授权条件是一致的,故此处使用 update 的授权判断即可:
resources/views/topics/show.blade.php
@can('update', $topic)
<div class="operate">
<hr>
<a href="{{ route('topics.edit', $topic->id) }}" class="btn btn-outline-secondary btn-sm" role="button">
<i class="far fa-edit"></i> 编辑
</a>
<form action="{{ route('topics.destroy', $topic->id) }}" method="post"
style="display: inline-block;"
onsubmit="return confirm('您确定要删除吗?');">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button type="submit" class="btn btn-outline-secondary btn-sm">
<i class="far fa-trash-alt"></i> 删除
</button>
</form>
</div>
@endcan