Laravel 中开发 CRUD 业务功能的步骤

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

猜你喜欢

转载自blog.csdn.net/Mr_Lewis/article/details/86670042