LC02 第 3 遍学习小结 -- 操作记录 5

由于LC02教程和实际需要开发的项目之间存在差异,所以第3遍学习只关注自己实际项目必须会用到的技能点。

第六章. 帖子的 CRUD

6.1 新建话题

本节开发xxx发布功能,允许注册用户发布xxx,发布完成后,跳转到xxx详情页面。

1) 新增入口

入口并不会跟教程一样,pass

2) 数据模型
设置 fillable 属性

3) 控制器
使用时再参考

4) 模型观察器
使用时再参考

5) 表单验证类
使用时再参考

6) 新建XX权限
使用时再参考

6.2 编辑器优化

1) 下载 Simditor

2) 集成到项目中

$ mkdir -p resources/editor/css
$ mkdir -p resources/editor/js

将下载的 simditor.css 放置于 resources/editor/css 文件夹,将 hotkeys.js, module.js, simditor.js, uploader.js 四个文件放置于 resources/editor/js 文件夹中。

修改 webpack.mix.js

const mix = require('laravel-mix');

mix.js('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css')
   .version()
   .copyDirectory('resources/editor/js', 'public/js')
   .copyDirectory('resources/editor/css', 'public/css');

3) 加载和渲染

这个非常重要!

vi resources/views/layouts/app.blade.php

.
.
.
  <!-- Styles -->
  <link href="{{ mix('css/app.css') }}" rel="stylesheet">

  @yield('styles')

</head>

<body>
.
.
.

  <!-- Scripts -->
  <script src="{{ mix('js/app.js') }}"></script>

  @yield('scripts')

</body>
</html>

接下来是页面调用:
resources/views/topics/create_and_edit.blade.php

.
.
.

@section('styles')
  <link rel="stylesheet" type="text/css" href="{{ asset('css/simditor.css') }}">
@stop

@section('scripts')
  <script type="text/javascript" src="{{ asset('js/module.js') }}"></script>
  <script type="text/javascript" src="{{ asset('js/hotkeys.js') }}"></script>
  <script type="text/javascript" src="{{ asset('js/uploader.js') }}"></script>
  <script type="text/javascript" src="{{ asset('js/simditor.js') }}"></script>

  <script>
    $(document).ready(function() {
      var editor = new Simditor({
        textarea: $('#editor'),
      });
    });
  </script>
@stop

版本控制

$ git add -A
$ git commit -m "WYSIWYG 编辑器"

6.3 编辑器内上传图片

1) 设置路由

routes/web.php

Route::post('upload_image', 'TopicsController@uploadImage')->name('topics.upload_image');

2) JS 脚本调用

resources/views/topics/create_and_edit.blade.php

.
.
.
@section('scripts')
  <script type="text/javascript" src="{{ asset('js/module.js') }}"></script>
  <script type="text/javascript" src="{{ asset('js/hotkeys.js') }}"></script>
  <script type="text/javascript" src="{{ asset('js/uploader.js') }}"></script>
  <script type="text/javascript" src="{{ asset('js/simditor.js') }}"></script>

  <script>
    $(document).ready(function() {
      var editor = new Simditor({
        textarea: $('#editor'),
        upload: {
          url: '{{ route('topics.upload_image') }}',
          params: {
            _token: '{{ csrf_token() }}'
          },
          fileKey: 'upload_file',
          connectionCount: 3,
          leaveConfirm: '文件上传中,关闭此页面将取消上传。'
        },
        pasteImage: true,
      });
    });
  </script>
@stop

3) 控制器图片处理

app/Http/Controllers/TopicsController.php

<?php
.
.
.

use App\Handlers\ImageUploadHandler;

class TopicsController extends Controller
{
    .
    .
    .
    public function uploadImage(Request $request, ImageUploadHandler $uploader)
    {
        // 初始化返回数据,默认是失败的
        $data = [
            'success'   => false,
            'msg'       => '上传失败!',
            'file_path' => ''
        ];
        // 判断是否有上传文件,并赋值给 $file
        if ($file = $request->upload_file) {
            // 保存图片到本地
            $result = $uploader->save($request->upload_file, 'topics', \Auth::id(), 1024);
            // 图片保存成功的话
            if ($result) {
                $data['file_path'] = $result['path'];
                $data['msg']       = "上传成功!";
                $data['success']   = true;
            }
        }
        return $data;
    }
}

4) 版本控制

同头像上传类似的,我们在上传图片的时候,程序自动创建了 public/uploads/images/topics/ 目录,此文件夹下的文件皆为用户上传的话题图片文件,我们需要防止这些文件被纳入 Git 版本控制器中,可以利用 Git 的 .gitignore 机制来实现:
public/uploads/images/topics/.gitignore

*
!.gitignore
$ git add -A
$ git commit -m "用户可以在编辑器内上传图片"

6.4 显示xxx

1) 路由和控制器

控制器的show方法。

2) 样式修复

resources/sass/app.scss

.
.
.

.simditor-body img {
  max-width:100%;
}

3) 样式调整

pass

6.5 XSS 安全漏洞

1) 安装 HTMLPurifier for Laravel 5

$ composer require "mews/purifier:~2.0"

2) 配置 HTMLPurifier for Laravel 5

$ php artisan vendor:publish --provider="Mews\Purifier\PurifierServiceProvider"

vi config/purifier.php

<?php

return [
    'encoding'      => 'UTF-8',
    'finalize'      => true,
    'cachePath'     => storage_path('app/purifier'),
    'cacheFileMode' => 0755,
    'settings'      => [
        'user_topic_body' => [
            'HTML.Doctype'             => 'XHTML 1.0 Transitional',
            'HTML.Allowed'             => 'div,b,strong,i,em,a[href|title],ul,ol,ol[start],li,p[style],br,span[style],img[width|height|alt|src],*[style|class],pre,hr,code,h2,h3,h4,h5,h6,blockquote,del,table,thead,tbody,tr,th,td',
            'CSS.AllowedProperties'    => 'font,font-size,font-weight,font-style,margin,width,height,font-family,text-decoration,padding-left,color,background-color,text-align',
            'AutoFormat.AutoParagraph' => true,
            'AutoFormat.RemoveEmpty'   => true,
        ],
    ],
];

3) 开始过滤

只需要在数据入库前进行过滤即可:
vi app/Observers/XxxObserver.php

<?php

namespace App\Observers;

use App\Models\Topic;

// creating, created, updating, updated, saving,
// saved,  deleting, deleted, restoring, restored

class TopicObserver
{
    public function saving(Topic $topic)
    {
        $topic->body = clean($topic->body, 'user_topic_body');

        $topic->excerpt = make_excerpt($topic->body);
    }
}

版本控制

$ git add -A
$ git commit -m "修复 XSS 注入漏洞"

6.6 编辑xxx

权限控制
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;
    }

    .
    .
    .
}

6.7 删除xxx

1) 权限控制

app/Policies/TopicPolicy.php

<?php
.
.
.

class TopicPolicy extends Policy
{
    .
    .
    .

    public function destroy(User $user, Topic $topic)
    {
        return $topic->user_id == $user->id;
    }
}

可以优化一下:
app/Models/User.php

<?php
.
.
.
class User extends Authenticatable implements MustVerifyEmailContract
{
    .
    .
    .

    public function isAuthorOf($model)
    {
        return $this->id == $model->user_id;
    }
}

重构下 TopicPolicy:
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 $user->isAuthorOf($topic);
    }

    public function destroy(User $user, Topic $topic)
    {
        return $user->isAuthorOf($topic);
    }
}

2) 构建删除表单

@can Blade 命令

resources/views/topics/show.blade.php

.
.
.

[@can](https://learnku.com/users/12729)('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
.
.
.

6.8 SEO友好的URL

不会用到,pass

6.9. 使用队列

队列在发送邮件时会用到

1) 配置队列

$ composer require "predis/predis:~1.1"

修改.env

QUEUE_CONNECTION=redis

2) 失败任务

$ php artisan queue:failed-table
$ php artisan migrate

3) 生成任务类

$ php artisan make:job xxx

具体代码参考

4) 任务分发

vi app/Observers/TopicObserver.php

// 推送任务到队列
dispatch(new TranslateSlug($topic));

5) 队列监控 Horizon

$ composer require "laravel/horizon:~1.3"
$ php artisan vendor:publish --provider="Laravel\Horizon\HorizonServiceProvider"

安装完毕,浏览器打开 http://xxx.test/horizon 访问控制台:

Horizon 是一个监控程序,需要常驻运行,我们可以通过以下命令启动:

$ php artisan horizon

安装了 Horizon 以后,我们将使用 horizon 命令来启动队列系统和任务监控,无需使用 queue:listen。

6) 线上部署须知

这个原来部署过,到时候参考一下即可。

6.10 小结

  • 如何嵌入WYSWYG编辑器
  • 如何在编辑器内上传图片
  • 如何解决XSS安全漏洞
  • 编辑、删除的权限控制
  • 构建删除表单?
  • 如何配置队列,建立失败任务,生成任务类,分发任务,队列监控,线上部署Horizon
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

社区文档:

将托管在 packagist.org 和 github.com 的扩展包使用国内 CDN 加速
GitHub Laravel 扩展包 TOP 250
速查表方便快速查询框架功能,支持手机访问,支持中英文版本
Laravel 中文文档,由社区用户翻译和维护,将会保持一直更新
此文档的目的,就是为了提高技术团队的凝聚力、一致性和生产效率。
开发环境的部署,开发者工具的选择,适用于 Mac 和 Windows。
浓缩过后的精华
Laravel Nova 后台管理面板文档的中文翻译
Lumen 中文文档,由社区用户翻译和维护,将会保持一直更新
Laravel 下知名扩展包 Dingo API 的中文文档,Laravel API 开发必知必会