• Laravel Vuejs 实战:开发知乎 (15)问题 Feed 和删除问题


    1.取全部数据:

    将all方法添加到QuestionRepository:

      1 <?php
      2 
      3 namespace AppRepositories;
      4 
      5 use AppModelsQuestion;
      6 use AppTopic;
      7 
      8 class QuestionRepository
      9 {
     10 
     11     /**
     12      * @return Question[]|IlluminateDatabaseEloquentCollection
     13      */
     14     public function all()
     15     {
     16         return Question::all();
     17     }
     18 
     19     /**
     20      * 根据提供的数据,使用Eloquent创建question,存储到数据库中并且返回该实例
     21      * @param array $data
     22      * @return mixed
     23      */
     24     public function create(array $data)
     25     {
     26         $question = Question::create($data);
     27         return $question;
     28     }
     29 
     30     /**
     31      * @param array $topics
     32      * @return array
     33      */
     34     public function normalizeTopics(array $topics)
     35     {
     36         //返回topic的id序列,如果不是数字,则强制认为是数据库中的topic的id,
     37         //这样虽然会漏掉用户如果设置的topic就是数字的情况,但是因为是测试,所以暂时忽略
     38         $normalizedTopics = collect($topics)->map(function ($topic) {
     39             if (is_numeric($topic))//是数字
     40             {
     41                 //在数据库中找id
     42                 $num_topic = Topic::query()->find($topic);
     43                 if (isset($num_topic) && $num_topic->count() > 0) //在数据库中找得到id
     44                 {
     45                     //因为是找到了 且是从question处提交,其内部的question_count应该增加1个
     46                     $num_topic->increment('questions_count');
     47                     //返回id
     48                     return (int)$num_topic->id;
     49                 }
     50             }
     51             //否则创建一个新的topic
     52             //再之前先判断是否找得到,因为有时候重复提交,select2在提交的数据中不为数字,但是实际上数据库中已经有值
     53             $already = Topic::query()->select('id', 'name')->where('name', '=', $topic);
     54 
     55             if ($already->count() > 0) {
     56                 //因为是找到了 且是从question处提交,其内部的question_count应该增加1个
     57                 $already->increment('questions_count');
     58                 //返回id
     59                 return $already->first()->id;
     60             }
     61             $data_of_topic = [
     62                 'name' => $topic,//目前view中topic只有一个选择框,没有设置content的输入框
     63                 'questions_count' => 1,//因为是找到了 且是从question处提交,其内部的question_count应该初始化为1个
     64             ];
     65             //入库
     66             $newTopic = Topic::create($data_of_topic);
     67             //返回id
     68             return $newTopic->id;
     69         })->toArray();
     70 
     71         return $normalizedTopics;
     72     }
     73 }
     74 
     75 
    QuestionRepository.php

    修改QuestionController index方法逻辑:

      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      * @return IlluminateContractsViewFactory|IlluminateViewView
     37      */
     38     public function index()
     39     {
     40         //
     41         $questions = $this->questionRepository->all();
     42         return view('questions.index', compact('questions'));
     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 
    QuestionController.php

    2.展示全部问题页面,创建index.blade.php

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

    3.再添加一个新建,【用户可以查看也可以新建】,附带问题属于的用户的数据:

    要添加模型关联【数据关联已经在question表里添加了 user_id】

    修改Question.php:

      1 <?php
      2 
      3 namespace AppModels;
      4 
      5 use AppTopic;
      6 use AppUser;
      7 use IlluminateDatabaseEloquentModel;
      8 
      9 class Question extends Model
     10 {
     11     //
     12     protected $fillable = ['title', 'content', 'user_id'];
     13 
     14     public function topics()
     15     {
     16         return $this->belongsToMany(
     17             Topic::class,
     18             'questions_topics' //表名我设置的是questions_topics,可能不是系统自动解析的question_topic
     19         )->withTimestamps();//withTimestamps操作questions_topics表中create_at及updated_at字段的
     20     }
     21  //添加user用户模型关联
     22     public function user()
     23     {
     24         return $this->belongsTo(User::class);
     25     }
     26 }
     27 
    Question.php

    user表里应该添加question_id字段

    执行:

      1 php artisan make:migration add_question_id_to_users_table --table=users

    修改****_add_question_id_to_users_table如下:

      1 <?php
      2 
      3 use IlluminateDatabaseMigrationsMigration;
      4 use IlluminateDatabaseSchemaBlueprint;
      5 use IlluminateSupportFacadesSchema;
      6 
      7 class AddQuestionIdToUsersTable extends Migration
      8 {
      9     /**
     10      * Run the migrations.
     11      *
     12      * @return void
     13      */
     14     public function up()
     15     {
     16         Schema::table('users', function (Blueprint $table) {
     17             //
     18             $table->bigInteger('question_id')->after('avatar')->nullable();
     19         });
     20     }
     21 
     22     /**
     23      * Reverse the migrations.
     24      *
     25      * @return void
     26      */
     27     public function down()
     28     {
     29         Schema::table('users', function (Blueprint $table) {
     30             //
     31             $table->dropColumn('question_id');
     32         });
     33     }
     34 }
     35 
    AddQuestionIdToUsersTable

    执行迁移:如果有不懂的可以查看 laravel 数据库操作(表、字段)

      1 php artisan migrate

    添加模型关联,修改User.php:

      1 <?php
      2 
      3 namespace App;
      4 
      5 use AppModelsQuestion;
      6 use IlluminateContractsAuthMustVerifyEmail;
      7 use IlluminateFoundationAuthUser as Authenticatable;
      8 use IlluminateNotificationsNotifiable;
      9 
     10 class User extends Authenticatable implements MustVerifyEmail
     11 {
     12     use Notifiable;
     13 
     14     /**
     15      * The attributes that are mass assignable.
     16      *
     17      * @var array
     18      */
     19     protected $fillable = [
     20         'name', 'email', 'password', 'avatar', 'activation_token'
     21     ];
     22 
     23     /**
     24      * The attributes that should be hidden for arrays.
     25      *
     26      * @var array
     27      */
     28     protected $hidden = [
     29         'password', 'remember_token',
     30     ];
     31 
     32     /**
     33      * The attributes that should be cast to native types.
     34      *
     35      * @var array
     36      */
     37     protected $casts = [
     38         'email_verified_at' => 'datetime',
     39     ];
     40 
     41     //添加用户模型和问题模型的模型关联
     42     public function questions()
     43     {
     44         return $this->hasMany(Question::class);
     45     }
     46 }
     47 
     48 
    User.php

    根据取得的问题数据,判断其中字段is_hidden确定此问题是否可以被其他用户查看【或者说是否允许发布在首页】


    3.Scope :

    参考:

    Laravel 模型使用scope前缀的方法

    Laravel中模型中可以定义scope开头方法,这类方法可以模型直接使用。这类方法也称作查询作用域

    例子:

    现有Post模型,内部定义一个scopeTitle()方法

      1  public function scopeTitle($query, $title="") {
      2         return $query->where('title', $title);
      3     }

    第一个参数不可省略。 第二个可以调用时传入。
    在控制器中使用该方法 title()

      1 $posts = Post::where('id', '<', 3)->title('test 2')->orderBy('id', 'desc')->get();

    Laravel模型的scope过滤:

    必须以scope开头。后面第一个字母大写。
    后面括号中第一个必须是Builder,第二个参数可以根据需要定义。
    返回值也必须是Builder


    Laravel 模型的 scope 普通用法和全局用法

    我们在Quesion.php添加一个scope方法:

      1 <?php
      2 
      3 namespace AppModels;
      4 
      5 use AppTopic;
      6 use AppUser;
      7 use IlluminateDatabaseEloquentModel;
      8 
      9 class Question extends Model
     10 {
     11     //
     12     protected $fillable = ['title', 'content', 'user_id'];
     13 
     14     public function topics()
     15     {
     16         return $this->belongsToMany(
     17             Topic::class,
     18             'questions_topics' //表名我设置的是questions_topics,可能不是系统自动解析的question_topic
     19         )->withTimestamps();//withTimestamps操作questions_topics表中create_at及updated_at字段的
     20     }
     21 
     22     public function user()
     23     {
     24         return $this->belongsTo(User::class);
     25     }
     26 
     27     /** scope+请求名命名的
     28      * @return bool
     29      */
     30     public function scopePublished($query)
     31     {
     32         return $query->where('is_hidden', 'F');//等于F表示不隐藏
     33     }
     34 }
     35 
    Question 中添加scope方法

    然后就可以在请求Question的时候使用这个方法,下面的代码添加到QuestionRepository中:

      1 public function getQuestionPublished()
      2 {
      3     return Question::published()
      4         ->latest('updated_at') //排序按照更新时间
      5         ->with('user')//带上用户模型 使用懒加载
      6         ->get();
      7 }
      8 

    QuestionRepository代码:

      1 <?php
      2 
      3 namespace AppRepositories;
      4 
      5 use AppModelsQuestion;
      6 use AppTopic;
      7 
      8 class QuestionRepository
      9 {
     10 
     11 
     12     public function all()
     13     {
     14         return Question::query()
     15             ->latest() //排序按照时间
     16             ->with('user')//带上用户模型 使用懒加载
     17             ->get();
     18     }
     19 
     20     public function getQuestionPublished()
     21     {
     22         return Question::published()
     23             ->latest('updated_at') //排序按照更新时间
     24             ->with('user')//带上用户模型 使用懒加载
     25             ->get();
     26     }
     27 
     28     /**
     29      * 根据提供的数据,使用Eloquent创建question,存储到数据库中并且返回该实例
     30      * @param array $data
     31      * @return mixed
     32      */
     33     public function create(array $data)
     34     {
     35         $question = Question::create($data);
     36         return $question;
     37     }
     38 
     39     /**
     40      * @param array $topics
     41      * @return array
     42      */
     43     public function normalizeTopics(array $topics)
     44     {
     45         //返回topic的id序列,如果不是数字,则强制认为是数据库中的topic的id,
     46         //这样虽然会漏掉用户如果设置的topic就是数字的情况,但是因为是测试,所以暂时忽略
     47         $normalizedTopics = collect($topics)->map(function ($topic) {
     48             if (is_numeric($topic))//是数字
     49             {
     50                 //在数据库中找id
     51                 $num_topic = Topic::query()->find($topic);
     52                 if (isset($num_topic) && $num_topic->count() > 0) //在数据库中找得到id
     53                 {
     54                     //因为是找到了 且是从question处提交,其内部的question_count应该增加1个
     55                     $num_topic->increment('questions_count');
     56                     //返回id
     57                     return (int)$num_topic->id;
     58                 }
     59             }
     60             //否则创建一个新的topic
     61             //再之前先判断是否找得到,因为有时候重复提交,select2在提交的数据中不为数字,但是实际上数据库中已经有值
     62             $already = Topic::query()->select('id', 'name')->where('name', '=', $topic);
     63 
     64             if ($already->count() > 0) {
     65                 //因为是找到了 且是从question处提交,其内部的question_count应该增加1个
     66                 $already->increment('questions_count');
     67                 //返回id
     68                 return $already->first()->id;
     69             }
     70             $data_of_topic = [
     71                 'name' => $topic,//目前view中topic只有一个选择框,没有设置content的输入框
     72                 'questions_count' => 1,//因为是找到了 且是从question处提交,其内部的question_count应该初始化为1个
     73             ];
     74             //入库
     75             $newTopic = Topic::create($data_of_topic);
     76             //返回id
     77             return $newTopic->id;
     78         })->toArray();
     79 
     80         return $normalizedTopics;
     81     }
     82 }
    QuestionRepository.PHP

    最后QuestionController中的 index方法为:

    现在就会隐藏掉is_hidden为T的question了

      1 public function index()
      2 {
      3     //
      4     $questions = $this->questionRepository->getQuestionPublished();
      5     return view('questions.index', compact('questions'));
      6 }
      7 

    再在index.blade.php中显示对应的用户信息即可:

      1 @extends('layouts.app')
      2 @section('content')
      3     <div class="contaier">
      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.store') }}" method="post">
     12                             {{--注意要有csrftoken--}}
     13                             @csrf
     14                             <div class="form-group">
     15                                 <label for="title">标题</label>
     16                                 <input type="text" name="title" class="form-control" placeholder="标题" id="title"
     17                                        value="{{ old('title') }}">
     18                                 <p class="text text-danger"> @error('title') {{ $message }} @enderror </p>
     19                             </div>
     20                             <!-- Select2 Topic Select -->
     21                             <div class="form-group">
     22                                 <label for="topic_list">选择主题</label>
     23                                 <select id="topic_list" class="js-example-basic-multiple form-control"
     24                                         name="topics[]" multiple></select>
     25                             </div>
     26                             <!-- 编辑器容器 -->
     27                             <script id="container" name="content" type="text/plain"
     28                                     style=" 100%">{!! old('content') !!}</script>
     29                             <p class="text text-danger"> @error('content') {{ $message }} @enderror </p>
     30                             <!--发布按钮-->
     31                             <button type="submit" class="btn btn-primary mt-2 float-md-right">发布问题</button>
     32                         </form>
     33                     </div>
     34                 </div>
     35 
     36                 @forelse($questions as $question)
     37                     <div class="card mt-4">
     38                         <div class="card-header">
     39                             <a href="{{ route('questions.show',$question) }}"
     40                                class="text text-primary">{{ $question->title }}</a>
     41                             <a href="{{ route('users.show',$question->user) }}"
     42                                class="btn btn-secondary">{{ $question->user->name }}</a>
     43                             @if(session()->has('warning'))
     44                                 <div class="alert alert-warning">{{ session()->get('warning') }}</div>
     45                             @endif
     46                             @can('update',$question)
     47                                 <a href="{{ route('questions.edit',$question) }}" class="btn btn-danger">编辑</a>
     48                             @endcan
     49                             @forelse($question->topics as $topic)
     50                                 <p class="badge badge-dark float-md-right m-1">{{ $topic->name }}</p>
     51                             @empty
     52                                 <p class="badge badge-warning float-md-right "> "No Topics"</p>
     53                             @endforelse
     54                         </div>
     55                         <div class="card-body">
     56                             {!! $question->content !!}
     57                         </div>
     58                     </div>
     59                 @empty
     60                     <p class="text text-danger">找不到问题</p>
     61                 @endforelse
     62             </div>
     63         </div>
     64     </div>
     65 @endsection
     66 @section('footer-js')
     67     @include('questions._footer_js')
     68 @endsection
    index.blade.php

    注意我们还没有创建用户的显示页面,所以可以暂时用#替换 {{ route('users.show',$question->user) }},就是替换:

      1 <a href="{{ route('users.show',$question->user) }}"
      2                                 class="btn btn-secondary">{{ $question->user->name }}</a>
      3 替换为:
      4 
      5 <a href="#"
      6                                 class="btn btn-secondary">{{ $question->user->name }}</a>
    index.blade.php

    效果:

    批注 2020-02-29 155105


    4.删除问题按钮 与 QuestionController处理方法destroy逻辑

    在问题详细页面,添加一个删除的入口:

    因为删除同样涉及到权限判定,所以在QuestionPolicy.php中添加:

      1 /**
      2  * 判断用户是否有权删除问题
      3  * @param User $user
      4  * @param Question $question
      5  * @return bool
      6  */
      7 public function destroy(User $user, Question $question)
      8 {
      9     return $user->id === $question->user_id;
     10 }
    
      1 @can('destroy',$question)
      2     <form action="{{ route('questions.destroy',$question) }}" method="post">
      3         @csrf
      4         @method('DELETE')
      5         <button type="submit" class="btn btn-danger">删除</button>
      6     </form>
      7 @endcan
      8 

    QuestionController中删除的逻辑为:

      1 /**Remove the specified resource from storage.
      2  * @param Question $question
      3  * @return IlluminateHttpRedirectResponse
      4  * @throws Exception
      5  */
      6 public function destroy(Question $question)
      7 {
      8     //
      9     if (auth()->user()->can('destroy', $question)) {
     10         $question->delete();
     11         return redirect()->route('questions.index')->with('success', "删除成功!");
     12     }
     13     return redirect()->back()->with('danger', "你不能删除不属于你的问题!");
     14 }
     15 

    注意我们将之前的

      1 <style scoped>
      2 .card-body img {
      3      100%;
      4 }
      5 </style>
      6 
      7 从show.blade.php中移除,在app.scss中添加:
      8 .card-body img {
      9      100%;
     10 }
     11 然后执行npm run dev
     12 
     13 

    index.blade.php代码为:

      1 @extends('layouts.app')
      2 @section('content')
      3     <div class="contaier">
      4         <div class="row">
      5             <div class="col-md-8 col-md offset-2">
      6                 @foreach(['success','warning','danger'] as $info)
      7                     @if(session()->has($info))
      8                         <div class="card">
      9                             <div class="card-body">
     10                                 <div class="alert alert-{{$info}}">{{ session()->get($info) }}</div>
     11                             </div>
     12                         </div>
     13                     @endif
     14                 @endforeach
     15                 <div class="card">
     16                     <div class="card-header">
     17                         发布问题
     18                     </div>
     19                     <div class="card-body">
     20                         <form action="{{ route('questions.store') }}" method="post">
     21                             {{--注意要有csrftoken--}}
     22                             @csrf
     23                             <div class="form-group">
     24                                 <label for="title">标题</label>
     25                                 <input type="text" name="title" class="form-control" placeholder="标题" id="title"
     26                                        value="{{ old('title') }}">
     27                                 <p class="text text-danger"> @error('title') {{ $message }} @enderror </p>
     28                             </div>
     29                             <!-- Select2 Topic Select -->
     30                             <div class="form-group">
     31                                 <label for="topic_list">选择主题</label>
     32                                 <select id="topic_list" class="js-example-basic-multiple form-control"
     33                                         name="topics[]" multiple></select>
     34                             </div>
     35                             <!-- 编辑器容器 -->
     36                             <script id="container" name="content" type="text/plain"
     37                                     style=" 100%">{!! old('content') !!}</script>
     38                             <p class="text text-danger"> @error('content') {{ $message }} @enderror </p>
     39                             <!--发布按钮-->
     40                             <button type="submit" class="btn btn-primary mt-2 float-md-right">发布问题</button>
     41                         </form>
     42                     </div>
     43                 </div>
     44 
     45                 @forelse($questions as $question)
     46                     <div class="card mt-4">
     47                         <div class="card-header">
     48                             <a href="{{ route('questions.show',$question) }}"
     49                                class="text text-primary">{{ $question->title }}</a>
     50                             <a href="#"
     51                                class="btn btn-secondary">{{ $question->user->name }}</a>
     52 
     53                             @can('update',$question)
     54                                 <a href="{{ route('questions.edit',$question) }}"
     55                                    class="btn btn-info">编辑</a>
     56                             @endcan
     57 
     58                             @can('destroy',$question)
     59                                 <form action="{{ route('questions.destroy',$question) }}" method="post">
     60                                     @csrf
     61                                     @method('DELETE')
     62                                     <button type="submit" class="btn btn-danger">删除</button>
     63                                 </form>
     64                             @endcan
     65 
     66                             @forelse($question->topics as $topic)
     67                                 <p class="badge badge-dark float-md-right m-1">{{ $topic->name }}</p>
     68                             @empty
     69                                 <p class="badge badge-warning float-md-right "> "No Topics"</p>
     70                             @endforelse
     71                         </div>
     72                         <div class="card-body">
     73                             {!! $question->content !!}
     74                         </div>
     75                     </div>
     76                 @empty
     77                     <p class="text text-danger">找不到问题</p>
     78                 @endforelse
     79             </div>
     80         </div>
     81     </div>
     82 @endsection
     83 @section('footer-js')
     84     @include('questions._footer_js')
     85 @endsection
    index.blade.php

    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 
     10                         @foreach(['success','warning','danger'] as $info)
     11                             @if(session()->has($info))
     12                                 <div class="alert alert-{{$info}}">{{ session()->get($info) }}</div>
     13                             @endif
     14                         @endforeach
     15 
     16                         @can('update',$question)
     17                             <a href="{{ route('questions.edit',$question) }}" class="btn btn-warning">编辑</a>
     18                         @endcan
     19 
     20                         @can('destroy',$question)
     21                             <form action="{{ route('questions.destroy',$question) }}" method="post">
     22                                 @csrf
     23                                 @method('DELETE')
     24                                 <button type="submit" class="btn btn-danger">删除</button>
     25                             </form>
     26                         @endcan
     27 
     28                         @forelse($question->topics as $topic)
     29                             <button class="btn btn-secondary float-md-right m-1">{{ $topic->name }}</button>
     30                         @empty
     31                             <p class="text text-warning float-md-right"> "No Topics"</p>
     32                         @endforelse
     33                     </div>
     34                     <div class="card-body">
     35                         {!! $question->content !!}
     36                     </div>
     37                 </div>
     38             </div>
     39         </div>
     40     </div>
     41 @endsection
    show.blade.php

    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      * 判断用户是否有权编辑更新问题
     27      * @param User $user
     28      * @param Question $question
     29      * @return bool
     30      */
     31     public function update(User $user, Question $question)
     32     {
     33         return $user->id === $question->user_id;
     34     }
     35 
     36 
     37     /**
     38      * 判断用户是否有权删除问题
     39      * @param User $user
     40      * @param Question $question
     41      * @return bool
     42      */
     43     public function destroy(User $user, Question $question)
     44     {
     45         return $user->id === $question->user_id;
     46     }
     47 }
    QuestionPolicy.php

    QuestionController.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      * @return IlluminateContractsViewFactory|IlluminateViewView
     37      */
     38     public function index()
     39     {
     40         //
     41         $questions = $this->questionRepository->getQuestionPublished();
     42         return view('questions.index', compact('questions'));
     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      * @param Question $question
    146      * @return IlluminateHttpRedirectResponse
    147      * @throws Exception
    148      */
    149     public function destroy(Question $question)
    150     {
    151         //
    152         if (auth()->user()->can('destroy', $question)) {
    153             $question->delete();
    154             return redirect()->route('questions.index')->with('success', "删除成功!");
    155         }
    156         return redirect()->back()->with('danger', "你不能删除不属于你的问题!");
    157     }
    158 
    159 
    160 }
    QuestionController.php

    5.支持软删除:

    关于软删除:

    Laravel软删除


    • 要让Eloquent模型支持软删除,首先在模型类中要使用SoftDeletestrait;
    • 此外还要设置$date属性数组,将deleted_at置于其中
    • 然后对应的数据库posts中添加deleted_at列,使用迁移来实现

    其他资料:

    Laravel 软删除操作  laravel 软删除softDelete

    Question.php:

      1 <?php
      2 
      3 namespace AppModels;
      4 
      5 use AppTopic;
      6 use AppUser;
      7 use IlluminateDatabaseEloquentModel;
      8 use IlluminateDatabaseEloquentSoftDeletes;
      9 
     10 class Question extends Model
     11 {
     12     //软删除 添加
     13     use SoftDeletes;
     14     //
     15     protected $fillable = ['title', 'content', 'user_id'];
     16     //支持软删除 添加
     17     protected $dates = ['deleted_at'];
     18 
     19     public function topics()
     20     {
     21         return $this->belongsToMany(
     22             Topic::class,
     23             'questions_topics' //表名我设置的是questions_topics,可能不是系统自动解析的question_topic
     24         )->withTimestamps();//withTimestamps操作questions_topics表中create_at及updated_at字段的
     25     }
     26 
     27     public function user()
     28     {
     29         return $this->belongsTo(User::class);
     30     }
     31 
     32     /** scope+请求名命名的
     33      * @return bool
     34      */
     35     public function scopePublished($query)
     36     {
     37         return $query->where('is_hidden', 'F');//等于F表示不隐藏
     38     }
     39 }


    执行命令:

      1 php artisan make:migration add_deleted_at_to_questions_table --table=questions

    修改****_add_deleted_at_to_questions_table.php文件:

      1 class AddDeletedAtToQuestionsTable extends Migration
      2 {
      3     /**
      4      * Run the migrations.
      5      *
      6      * @return void
      7      */
      8     public function up()
      9     {
     10         Schema::table('questions', function (Blueprint $table) {
     11             //
     12             $table->softDeletes();
     13         });
     14     }
     15 
     16     /**
     17      * Reverse the migrations.
     18      *
     19      * @return void
     20      */
     21     public function down()
     22     {
     23         Schema::table('questions', function (Blueprint $table) {
     24             //
     25         });
     26     }
     27 }
     28 

    执行

      1 php artisan migrate

    可以看到软删除会在deleted_at列存储时间,但数据并未真正删除:

    批注 2020-02-29 202302

  • 相关阅读:
    HOWTO re
    数据类型
    字符串
    最大公约数
    this
    tip 2:找最小公倍数之Boost
    tip 1:一个简单的将int型转换成char的方法
    Item47
    成员函数模板
    item44:将与参数无关的代码抽离template
  • 原文地址:https://www.cnblogs.com/dzkjz/p/12383060.html
Copyright © 2020-2023  润新知