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
日拱一卒
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!