zoukankan      html  css  js  c++  java
  • Laravel Vuejs 实战:开发知乎 (13)实现编辑问题

    1.在question的show blade文件中提供编辑问题的入口【判断用户是否有权编辑,没有权限不显示编辑按钮】:

    使用Policy,执行命令:

      1 php artisan make:Policy QuestionPolicy

    生成 QuestionPolicy.php文件:

      1 <?php
      2 
      3 namespace AppPolicies;
      4 
      5 use AppModelsQuestion;
      6 use AppUser;
      7 use IlluminateAuthAccessHandlesAuthorization;
      8 
      9 class QuestionPolicy
     10 {
     11     use HandlesAuthorization;
     12 
     13     /**
     14      * Create a new policy instance.
     15      *
     16      * @return void
     17      */
     18     public function __construct()
     19     {
     20         //
     21     }
     22 
     23 
     24     /**
     25      * 判断用户是否有权编辑更新问题
     26      * @param User $user
     27      * @param Question $question
     28      * @return bool
     29      */
     30     public function update(User $user, Question $question)
     31     {
     32         return $user->id === $question->user_id;
     33     }
     34 }
    QuestionPolicy.php

    AuthServiceProviderpolicies 数组属性里添加授权映射关系:

      1 <?php
      2 
      3 namespace AppProviders;
      4 
      5 use AppModelsQuestion;
      6 use AppPoliciesQuestionPolicy;
      7 use IlluminateFoundationSupportProvidersAuthServiceProvider as ServiceProvider;
      8 use IlluminateSupportFacadesGate;
      9 
     10 class AuthServiceProvider extends ServiceProvider
     11 {
     12     /**
     13      * The policy mappings for the application.
     14      *
     15      * @var array
     16      */
     17     protected $policies = [
     18         // 'AppModel' => 'AppPoliciesModelPolicy',
     19         //添加授权映射关系
     20         Question::class => QuestionPolicy::class,
     21     ];
     22 
     23     /**
     24      * Register any authentication / authorization services.
     25      *
     26      * @return void
     27      */
     28     public function boot()
     29     {
     30         $this->registerPolicies();
     31 
     32         //
     33     }
     34 }
     35 
     36 
    AuthServiceProvider.php

    使用方法:

    视图中:

      1 @can('update', $question)
      2 <a href="#">编辑</a>
      3 @endcan

    Controller文件中:

      1 auth()->user()->can('update', $question)

    关于Policy可以参考:Laravel 用户授权 Gate和Policy  ,Laravel Policy 使用Laravel-权限系统


    2.在请求编辑交由QuestionController edit方法处理的时候,判断用户是否有权编辑,返回问题编辑的页面view,并传入question参数,

      1 /**判断权限 返回视图
      2  * @param Question $question
      3  * @return IlluminateContractsViewFactory|IlluminateHttpRedirectResponse|IlluminateViewView
      4  */
      5 public function edit(Question $question)
      6 {
      7     if (auth()->user()->can('update', $question)) //判断当前用户是否有权编辑更新该question实例
      8     {
      9         //返回编辑视图
     10         return view('questions.edit', compact('question'));
     11     } else {
     12         //返回警告 没有权限
     13         return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
     14     }
     15 }
     16 

    跳转没有权限需要给一个提示给用户,所以show.blade.php修改如下:

      1 @extends('layouts.app')
      2 @section('content')
      3     <div class="container">
      4         <div class="row">
      5             <div class="col-md-8 col-md offset-2">
      6                 <div class="card">
      7                     <div class="card-header">
      8                         {{ $question->title }}
      9                         @if(session()->has('warning'))
     10                             <div class="alert alert-warning">{{ session()->get('warning') }}</div>
     11                         @endif
     12                         @can('update',$question)
     13                             <a href="{{ route('questions.edit',$question) }}" class="btn btn-danger">编辑</a>
     14                         @endcan
     15                         @forelse($question->topics as $topic)
     16                             <button class="btn btn-secondary float-md-right m-1">{{ $topic->name }}</button>
     17                         @empty
     18                             <p class="text text-warning float-md-right"> "No Topics"</p>
     19                         @endforelse
     20                     </div>
     21                     <div class="card-body">
     22                         {!! $question->content !!}
     23                     </div>
     24                 </div>
     25             </div>
     26         </div>
     27     </div>
     28     <style scoped>
     29         .card-body img {
     30              100%;
     31         }
     32     </style>
     33 @endsection
    show.blade.php

    3.展示question edit编辑视图view

    如果有权限的,视图如下:

      1 @extends('layouts.app')
      2 @section('content')
      3     <div class="container">
      4         <div class="row">
      5             <div class="col-md-8 col-md-offset-2">
      6                 <div class="card">
      7                     <div class="card-header">
      8                         发布问题
      9                     </div>
     10                     <div class="card-body">
     11                         <form action="{{ route('questions.update',$question) }}" method="post">
     12                             {{--注意要有csrftoken--}}
     13                             @csrf
     14                             @method('PUT')
     15                             <div class="form-group">
     16                                 <label for="title">标题</label>
     17                                 <input type="text" name="title" class="form-control" placeholder="标题" id="title"
     18                                        value="{{ $question->title }}">
     19                                 <p class="text text-danger"> @error('title') {{ $message }} @enderror </p>
     20                             </div>
     21                             <!-- Select2 Topic Select -->
     22                             <div class="form-group">
     23                                 <label for="topic_list">选择主题</label>
     24                                 <select id="topic_list" class="js-example-basic-multiple form-control"
     25                                         name="topics[]" multiple>
     26                                     @forelse( $question->topics as $topic )
     27                                         <option value="{{ $topic->id }}" selected>{{ $topic->name }}</option>
     28                                     @empty
     29                                     @endforelse
     30                                 </select>
     31                             </div>
     32                             <!-- 编辑器容器 -->
     33                             <script id="container" name="content" type="text/plain"
     34                                     style=" 100%">{!! $question->content !!}</script>
     35                             <p class="text text-danger"> @error('content') {{ $message }} @enderror </p>
     36                             <!--发布按钮-->
     37                             <button type="submit" class="btn btn-primary mt-2 float-md-right">更新问题</button>
     38                         </form>
     39                     </div>
     40                 </div>
     41             </div>
     42         </div>
     43     </div>
     44 @endsection
     45 @section('footer-js')
     46     <script type="text/javascript">
     47         // 实例化编辑器
     48         var ue = UE.getEditor('container', {
     49             toolbars: [
     50                 ['bold', 'italic', 'underline', 'strikethrough', 'blockquote', 'insertunorderedlist', 'insertorderedlist', 'justifyleft', 'justifycenter', 'justifyright', 'link', 'insertimage', 'fullscreen']
     51             ],
     52             elementPathEnabled: false,
     53             enableContextMenu: false,
     54             autoClearEmptyNode: true,
     55             wordCount: false,
     56             imagePopup: false,
     57             autotypeset: {indent: true, imageBlockLine: 'center'}
     58         });
     59         ue.ready(function () {
     60             ue.execCommand('serverparam', '_token', '{{ csrf_token() }}'); // 设置 CSRF token.
     61         });
     62         $(document).ready(function () {
     63             // Select2多选js
     64             $('.js-example-basic-multiple').select2({
     65                 // 设置属性及初始化值
     66                 tags: true,
     67                 placeholder: '选择相关话题',
     68                 miniumInputLength: 2,
     69 
     70                 ajax: {
     71                     url: '/api/topics',
     72                     dataType: 'json',
     73                     // Additional AJAX parameters go here; see the end of this chapter for the full code of this example
     74                     delay: 250,
     75                     data: function (params) {
     76                         return {
     77                             // term : The current search term in the search box.
     78                             //     q : Contains the same contents as term.
     79                             // _type: A "request type". Will usually be query, but changes to query_append for paginated requests.
     80                             // page : The current page number to request. Only sent for paginated (infinite scrolling) searches.
     81                             q: params.term, // search term
     82                             // page: params.page 暂时不需要分页
     83                         };
     84                     },
     85                     processResults: function (data, params) {
     86                         // 解析结果为Select2期望的格式
     87                         // parse the results into the format expected by Select2
     88                         // since we are using custom formatting functions we do not need to
     89                         // alter the remote JSON data, except to indicate that infinite
     90                         // scrolling can be used
     91                         return {
     92                             results: data
     93                         };
     94                     },
     95                     cache: true,
     96                 },
     97                 //模板样式
     98                 templateResult: formatTopic,
     99                 //模板样式 【选择项】
    100                 templateSelection: formatTopicSelection,
    101                 escapeMarkup: function (markup) {
    102                     return markup;
    103                 }
    104             });
    105         });
    106 
    107         //格式化话题
    108         function formatTopic(topic) {
    109             return "<div class='select2-result-repository clearfix'>" +
    110             "<div class='select2-result-repository__meta'>" +
    111             "<div class='select2-result-repository__title'>" +
    112             topic.name ? topic.name : "Laravel" +
    113                 "</div></div></div>";
    114         }
    115 
    116         //格式化话题选项
    117         function formatTopicSelection(topic) {
    118             return topic.name || topic.text;
    119         }
    120     </script>
    121 @endsection
    edit.blade.php

    更改说明:

    批注 2020-02-29 134625

    我们原样输出了之前问题里的数据,然后使用method put来进行更新【method域】,更改了action 属性值为questions.update路由,路由传入了$question参数

    注:option因为是用户之前已经选过,所以要selected

    4.提交更新结果到QuestionController的update方法内部处理逻辑:

      1 /** Update the specified resource in storage.
      2  * @param QuestionStoreRequest $questionStoreRequest
      3  * @param Question $question
      4  * @return IlluminateHttpRedirectResponse
      5  */
      6 public function update(QuestionStoreRequest $questionStoreRequest, Question $question)
      7 {
      8     //更新前 判断下权限
      9     if (!(auth()->user()->can('update', $question))) {
     10         //返回警告 没有权限
     11         return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
     12     }
     13 
     14     //取得更新的字段 使用Eloquent提供的update方法执行问题更新
     15     $question->update([
     16         'title' => $questionStoreRequest->get('title'),
     17         'content' => $questionStoreRequest->get('content'),
     18     ]);
     19 
     20 
     21     //topics的操作这时候看起来有点臃肿 可以使用TopicController来管理,暂时省略
     22     //存储topics
     23     $topics = $this->questionRepository->normalizeTopics($questionStoreRequest->get('topics'));
     24     //使用我们再question model里面添加的topics方法获得 topics关联,
     25     //再使用sync方法同步tag 【删除的会被删除掉,没删除的就保留,新的就增加】
     26     $question->topics()->sync($topics);
     27 
     28     //更新完成,跳转回去
     29     return redirect()->back();
     30 }
    QuestionController中update方法

    注:可以参考 推荐使用 更新一下授权判断:

      1 //更新前 判断下权限
      2 if (!(auth()->user()->can('update', $question))) {
      3     //返回警告 没有权限
      4     return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
      5 }
      6 

    改为

      1 //更新前 判断下权限
      2 if (!($this->authorize('update', $question))) {
      3     //返回警告 没有权限
      4     return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
      5 }

    这样用户不具有更新问题的权限时候会直接跳403权限不足:

    批注 2020-02-29 140138

    更新后代码:

      1 <?php
      2 
      3 namespace AppHttpControllers;
      4 
      5 use AppHttpRequestsQuestionStoreRequest;
      6 use AppModelsQuestion;
      7 use AppRepositoriesQuestionRepository;
      8 
      9 
     10 class QuestionController extends Controller
     11 {
     12 
     13     /**
     14      * @var QuestionRepository
     15      */
     16     private $questionRepository;
     17 
     18     public function __construct(QuestionRepository $questionRepository)
     19     {
     20         $this->middleware(
     21             'auth',
     22             [
     23                 'except' =>
     24                     [
     25                         'index',
     26                         'show',
     27                     ]//非注册用户只能查看不能编辑添加更改删除
     28             ]
     29         );
     30 
     31         $this->questionRepository = $questionRepository;
     32     }
     33 
     34     /**
     35      * Display a listing of the resource.
     36      *
     37      * @return IlluminateHttpResponse
     38      */
     39     public function index()
     40     {
     41         //
     42 
     43     }
     44 
     45 
     46     /**
     47      * @return IlluminateContractsViewFactory|IlluminateViewView
     48      */
     49     public function create()
     50     {
     51         //
     52         return view('questions.create');
     53     }
     54 
     55 
     56     /**
     57      * @param QuestionStoreRequest $request
     58      * @return IlluminateHttpRedirectResponse
     59      */
     60     public function store(QuestionStoreRequest $request)//依赖注入QuestionStoreRequest实例
     61     {
     62         //
     63 //        $data = $request->validate([
     64 //            'title' => 'required|min:8',
     65 //            'content' => 'required|min:28',
     66 //        ]);
     67         //存储topics
     68         $topics = $this->questionRepository->normalizeTopics($request->get('topics'));
     69         //初始化question要用到的数据
     70         $data = $request->all();
     71         $data['user_id'] = auth()->user()->id;
     72 
     73 //        $question=Question::create($data); 被下方代码取代
     74         $question = $this->questionRepository->create($data);
     75 
     76         //使用我们再question model里面添加的topics方法获得 topics关联,再使用attach方法
     77         $question->topics()->attach($topics);
     78 
     79         return redirect()->route('questions.show', $question);
     80     }
     81 
     82 
     83     /**
     84      * @param Question $question
     85      * @return IlluminateContractsViewFactory|IlluminateViewView
     86      */
     87     public function show(Question $question)
     88     {
     89         //使用关系关联加载,with方法会将分类之下的商品一起查询出来,而且不会出现N+1影响性能的问题
     90         $question->with('topics')->get();
     91 
     92         return view('questions.show', compact('question'));
     93     }
     94 
     95 
     96     /**判断权限 返回视图
     97      * @param Question $question
     98      * @return IlluminateContractsViewFactory|IlluminateHttpRedirectResponse|IlluminateViewView
     99      */
    100     public function edit(Question $question)
    101     {
    102         if ($this->authorize('update', $question)) //判断当前用户是否有权编辑更新该question实例
    103         {
    104             //返回编辑视图
    105             return view('questions.edit', compact('question'));
    106         } else {
    107             //返回警告 没有权限
    108             return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
    109         }
    110     }
    111 
    112 
    113     /** Update the specified resource in storage.
    114      * @param QuestionStoreRequest $questionStoreRequest
    115      * @param Question $question
    116      * @return IlluminateHttpRedirectResponse
    117      */
    118     public function update(QuestionStoreRequest $questionStoreRequest, Question $question)
    119     {
    120         //更新前 判断下权限
    121         if (!($this->authorize('update', $question))) {
    122             //返回警告 没有权限
    123             return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
    124         }
    125 
    126         //取得更新的字段 使用Eloquent提供的update方法执行问题更新
    127         $question->update([
    128             'title' => $questionStoreRequest->get('title'),
    129             'content' => $questionStoreRequest->get('content'),
    130         ]);
    131 
    132 
    133         //topics的操作这时候看起来有点臃肿 可以使用TopicController来管理,暂时省略
    134         //存储topics
    135         $topics = $this->questionRepository->normalizeTopics($questionStoreRequest->get('topics'));
    136         //使用我们再question model里面添加的topics方法获得 topics关联,
    137         //再使用sync方法同步tag 【删除的会被删除掉,没删除的就保留,新的就增加】
    138         $question->topics()->sync($topics);
    139 
    140         //更新完成,跳转回去
    141         return redirect()->back();
    142     }
    143 
    144     /**
    145      * Remove the specified resource from storage.
    146      *
    147      * @param int $id
    148      * @return IlluminateHttpResponse
    149      */
    150     public function destroy($id)
    151     {
    152         //
    153     }
    154 
    155 
    156 }
    157 
    QuestionController.php

    我这里暂时不去实现authorized失败之后页面的定制,所以没有使用$this->authorize方法。

    最后QuestionContoller.php代码如下:

      1 <?php
      2 
      3 namespace AppHttpControllers;
      4 
      5 use AppHttpRequestsQuestionStoreRequest;
      6 use AppModelsQuestion;
      7 use AppRepositoriesQuestionRepository;
      8 
      9 
     10 class QuestionController extends Controller
     11 {
     12 
     13     /**
     14      * @var QuestionRepository
     15      */
     16     private $questionRepository;
     17 
     18     public function __construct(QuestionRepository $questionRepository)
     19     {
     20         $this->middleware(
     21             'auth',
     22             [
     23                 'except' =>
     24                     [
     25                         'index',
     26                         'show',
     27                     ]//非注册用户只能查看不能编辑添加更改删除
     28             ]
     29         );
     30 
     31         $this->questionRepository = $questionRepository;
     32     }
     33 
     34     /**
     35      * Display a listing of the resource.
     36      *
     37      * @return IlluminateHttpResponse
     38      */
     39     public function index()
     40     {
     41         //
     42 
     43     }
     44 
     45 
     46     /**
     47      * @return IlluminateContractsViewFactory|IlluminateViewView
     48      */
     49     public function create()
     50     {
     51         //
     52         return view('questions.create');
     53     }
     54 
     55 
     56     /**
     57      * @param QuestionStoreRequest $request
     58      * @return IlluminateHttpRedirectResponse
     59      */
     60     public function store(QuestionStoreRequest $request)//依赖注入QuestionStoreRequest实例
     61     {
     62         //
     63 //        $data = $request->validate([
     64 //            'title' => 'required|min:8',
     65 //            'content' => 'required|min:28',
     66 //        ]);
     67         //存储topics
     68         $topics = $this->questionRepository->normalizeTopics($request->get('topics'));
     69         //初始化question要用到的数据
     70         $data = $request->all();
     71         $data['user_id'] = auth()->user()->id;
     72 
     73 //        $question=Question::create($data); 被下方代码取代
     74         $question = $this->questionRepository->create($data);
     75 
     76         //使用我们再question model里面添加的topics方法获得 topics关联,再使用attach方法
     77         $question->topics()->attach($topics);
     78 
     79         return redirect()->route('questions.show', $question);
     80     }
     81 
     82 
     83     /**
     84      * @param Question $question
     85      * @return IlluminateContractsViewFactory|IlluminateViewView
     86      */
     87     public function show(Question $question)
     88     {
     89         //使用关系关联加载,with方法会将分类之下的商品一起查询出来,而且不会出现N+1影响性能的问题
     90         $question->with('topics')->get();
     91 
     92         return view('questions.show', compact('question'));
     93     }
     94 
     95 
     96     /**判断权限 返回视图
     97      * @param Question $question
     98      * @return IlluminateContractsViewFactory|IlluminateHttpRedirectResponse|IlluminateViewView
     99      */
    100     public function edit(Question $question)
    101     {
    102         if (auth()->user()->can('update', $question)) //判断当前用户是否有权编辑更新该question实例
    103         {
    104             //返回编辑视图
    105             return view('questions.edit', compact('question'));
    106         } else {
    107             //返回警告 没有权限
    108             return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
    109         }
    110     }
    111 
    112 
    113     /** Update the specified resource in storage.
    114      * @param QuestionStoreRequest $questionStoreRequest
    115      * @param Question $question
    116      * @return IlluminateHttpRedirectResponse
    117      */
    118     public function update(QuestionStoreRequest $questionStoreRequest, Question $question)
    119     {
    120         //更新前 判断下权限
    121         if (!(auth()->user()->can('update', $question))) {
    122             //返回警告 没有权限
    123             return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
    124         }
    125         //取得更新的字段 使用Eloquent提供的update方法执行问题更新
    126         $question->update([
    127             'title' => $questionStoreRequest->get('title'),
    128             'content' => $questionStoreRequest->get('content'),
    129         ]);
    130 
    131 
    132         //topics的操作这时候看起来有点臃肿 可以使用TopicController来管理,暂时省略
    133         //存储topics
    134         $topics = $this->questionRepository->normalizeTopics($questionStoreRequest->get('topics'));
    135         //使用我们再question model里面添加的topics方法获得 topics关联,
    136         //再使用sync方法同步tag 【删除的会被删除掉,没删除的就保留,新的就增加】
    137         $question->topics()->sync($topics);
    138 
    139         //更新完成,跳转回去
    140         return redirect()->back();
    141     }
    142 
    143     /**
    144      * Remove the specified resource from storage.
    145      *
    146      * @param int $id
    147      * @return IlluminateHttpResponse
    148      */
    149     public function destroy($id)
    150     {
    151         //
    152     }
    153 
    154 
    155 }
    156 
    157 
    QuestionController.php

    效果示例:

    打开 http://zhihu.test/questions/16

    批注 2020-02-29 141216

    打开 http://zhihu.test/questions/16/edit

    批注 2020-02-29 141246

    打开没有权限的示例【我用tinker设置了user_id=10,当前用户id是3】:

    打开http://zhihu.test/questions/18 可以看到,没有编辑按钮

    批注 2020-02-29 141421

    手动输入url打开http://zhihu.test/questions/18/edit 看到提示,【注意代码bug因为上面有redirect()->back()如果之前是从http://zhihu.test/questions/18/edit 打开 然后一直会跳回 导致请求跳转过多异常,不过实际使用这种情况很少见这里就忽略了】

    批注 2020-02-29 141332

  • 相关阅读:
    sql server 数据库还原后sa连接不上原因
    A potentially dangerous Request.Form value was detected from the client的解决办法
    单引号引发的血案
    再次拿起live writer
    临汾第一站软件工作室网站建成运行……
    集思广益,求工作室名称
    IT人们给个建议
    博客园有没有改版的必要
    20151213调转页面以及页面传值
    20151018网页大纲
  • 原文地址:https://www.cnblogs.com/dzkjz/p/12382742.html
Copyright © 2011-2022 走看看