Laravel Vuejs 实战:开发知乎 (17)实现提交答案
1.添加路由 并 处理AnswerController内逻辑:
路由:向web.php中添加:
1 #region 回答路由CRUD 2 3 //查看回答 以及 回答的form 都是在questions详细内容页面 4 5 //提交回答 6 Route::post('questions/{question}/answers', 'AnswerController@store')->name('answers.store'); 7 8 //更新回答 9 10 11 //删除回答 12 13 14 #endregion 15
1 <?php 2 3 /* 4 |-------------------------------------------------------------------------- 5 | Web Routes 6 |-------------------------------------------------------------------------- 7 | 8 | Here is where you can register web routes for your application. These 9 | routes are loaded by the RouteServiceProvider within a group which 10 | contains the "web" middleware group. Now create something great! 11 | 12 */ 13 14 Route::get('/', function () { 15 return view('welcome'); 16 }); 17 18 Auth::routes(['verify' => true]); 19 20 Route::get('/home', 'HomeController@index')->name('home'); 21 22 Route::resource('questions', 'QuestionController'); 23 24 25 #region 回答路由CRUD 26 27 //查看回答 以及 回答的form 都是在questions详细内容页面 28 29 //提交回答 30 Route::post('questions/{question}/answers', 'AnswerController@store')->name('answers.store'); 31 32 //更新回答 33 34 35 //删除回答 36 37 38 #endregion 39 40
AnswerController.php最后更新见文末
2.在question详情内页 添加提交回答的form
最后更新见文末 show.blade.php
3.提交后由于要给Question和User模型内赋值 answer_id 所以在$fillable属性里加上'answer_id':
不赋值!见后文根据 一对多关系处说明。不存所以表里之前的字段要删除,也就更不可能赋值了
在处理一对多的时候
Php Laravel框架 多表关系处理 之 Eloquent一对多关系处理
Laravel One to Many Eloquent Relationship Tutorial
多对多的时候:
一对多关系,在创建answer模型的时候,需要更新user 和 question 里面的 answer_id,
不需要更新!见后文根据 一对多关系处说明。不存所以表里之前的字段要删除,也就更不可能需要更新了
之前忘了在anwers 的 migration里加外键;
【模型之间通过模型关联方法 已经默认配置了外键 讲解:一对多】
laravel模型表建立外键约束的使用:
模型:
1 //表->posts 2 class Post extends Model 3 { 4 //关联用户: 5 public function user(){ 6 //belongsTo,第一个参数:外键表,第二个:当前表的外键,第三个:外渐表的主键。 7 //如果第二个是名词+id,第三个是:id。后面的两个参数可以省略: 8 // return $this->belongsTo('App\User'); 9 return $this->belongsTo('App\User','user_id','id'); 10 } 11 } 12视图中的调用:下面的会输出:posts表中user_id对应的user表中的id的name值
1 <a href="#">{{$post->user->name}}</a></p>
现在加上,执行命令:
1 php artisan make:migration add_foreign_key_relationship_to_answers_table --table=answers
注意是answers表;
1 <?php 2 3 use Illuminate\Database\Migrations\Migration; 4 use Illuminate\Database\Schema\Blueprint; 5 use Illuminate\Support\Facades\Schema; 6 7 class AddForeignKeyRelationshipToAnswersTable extends Migration 8 { 9 /** 10 * Run the migrations. 11 * 12 * @return void 13 */ 14 public function up() 15 { 16 Schema::table('answers', function (Blueprint $table) { 17 // 18 $table->foreign('user_id')->references('id')->on('users'); 19 $table->foreign('question_id')->references('id')->on('questions'); 20 }); 21 } 22 23 /** 24 * Reverse the migrations. 25 * 26 * @return void 27 */ 28 public function down() 29 { 30 Schema::table('answers', function (Blueprint $table) { 31 // 32 $table->dropForeign('answers_user_id_foreign'); 33 $table->dropForeign('answers_question_id_foreign'); 34 }); 35 } 36 } 37 38
注意foreign key的命名:answers_user_id_foreign 查阅: Foreign Key Constraints
然后执行:
1 php artisan migrate
根据 一对多关系:
主表不存子表的id:用户不存回答的id,问题不存回答的id,
子表存储主表的id:回答存储用户的id,回答存储问题的id,并设置外键,可以使用save saveMany associate再save方法;
具体请查阅: Laravel One to Many Eloquent Relationship Tutorial
所以我们需要移除之前再Quesiton和User表中的answer_id字段;代码省略
多对多的表:
新建立第三个表 还要使用attach sync detach方法操作更新
以上都必须设置模型关联
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 {{--问题--}} 7 <div class="card"> 8 <div class="card-header"> 9 {{ $question->title }} 10 11 @foreach(['success','warning','danger'] as $info) 12 @if(session()->has($info)) 13 <div class="alert alert-{{$info}}">{{ session()->get($info) }}</div> 14 @endif 15 @endforeach 16 17 @can('update',$question) 18 <a href="{{ route('questions.edit',$question) }}" class="btn btn-warning">编辑</a> 19 @endcan 20 21 @can('destroy',$question) 22 <form action="{{ route('questions.destroy',$question) }}" method="post"> 23 @csrf 24 @method('DELETE') 25 <button type="submit" class="btn btn-danger">删除</button> 26 </form> 27 @endcan 28 29 @forelse($question->topics as $topic) 30 <button class="btn btn-secondary float-md-right m-1">{{ $topic->name }}</button> 31 @empty 32 <p class="text text-warning float-md-right"> "No Topics"</p> 33 @endforelse 34 35 <p class="text text-info float-md-right"> 已有{{ count($question->answers) }}个回答</p> 36 37 </div> 38 <div class="card-body"> 39 {!! $question->content !!} 40 </div> 41 </div> 42 43 44 {{--回答提交form--}} 45 46 <div class="card mt-2"> 47 <div class="card-header"> 48 提交回答 49 </div> 50 <div class="card-body"> 51 <form action="{{ route('answers.store',$question) }}" method="post"> 52 @csrf 53 <!-- 回答编辑器容器 --> 54 <script id="container" name="content" type="text/plain" 55 style="width: 100%;height: 200px">{!! old('content') !!}</script> 56 <p class="text text-danger"> @error('content') {{ $message }} @enderror </p> 57 <!--提交按钮--> 58 <button type="submit" class="btn btn-primary mt-2 float-md-right">提交回答</button> 59 </form> 60 </div> 61 </div> 62 63 {{--展示答案--}} 64 @forelse($question->answers as $answer) 65 <div class="card mt-4"> 66 <div class="card-header"> 67 <div class="float-left"> 68 <img src="{{ $answer->user->avatar }}" class="img-thumbnail imgWrap" 69 style="height: 50px" alt="{{ $answer->user->name }}"> 70 <span class="text text-info">{{ $answer->user->name }}</span> 71 </div> 72 <span class="float-right text text-info m-auto">{{ $answer->updated_at }}</span> 73 </div> 74 75 <div class="card-body"> 76 {!! $answer->content !!} 77 </div> 78 </div> 79 80 @empty 81 82 @endforelse 83 </div> 84 </div> 85 </div> 86 @endsection 87 @section('footer-js') 88 @include('questions._footer_js') 89 @endsection
QuestionController.php:
1 <?php 2 3 namespace App\Http\Controllers; 4 5 use App\Http\Requests\QuestionStoreRequest; 6 use App\Models\Question; 7 use App\Repositories\QuestionRepository; 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 \Illuminate\Contracts\View\Factory|\Illuminate\View\View 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 \Illuminate\Contracts\View\Factory|\Illuminate\View\View 48 */ 49 public function create() 50 { 51 // 52 return view('questions.create'); 53 } 54 55 56 /** 57 * @param QuestionStoreRequest $request 58 * @return \Illuminate\Http\RedirectResponse 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 \Illuminate\Contracts\View\Factory|\Illuminate\View\View 86 */ 87 public function show(Question $question) 88 { 89 //使用关系关联加载,with方法会将分类之下的主题一起查询出来,而且不会出现N+1影响性能的问题 90 $question->with('topics')->get(); 91 //使用关系关联加载,with方法会将分类之下的回答一起查询出来,而且不会出现N+1影响性能的问题 92 $question->with('answers')->get(); 93 94 return view('questions.show', compact('question')); 95 } 96 97 98 /**判断权限 返回视图 99 * @param Question $question 100 * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\View\View 101 */ 102 public function edit(Question $question) 103 { 104 if (auth()->user()->can('update', $question)) //判断当前用户是否有权编辑更新该question实例 105 { 106 //返回编辑视图 107 return view('questions.edit', compact('question')); 108 } else { 109 //返回警告 没有权限 110 return redirect()->back()->with('warning', '你不能编辑不属于你的问题!'); 111 } 112 } 113 114 115 /** Update the specified resource in storage. 116 * @param QuestionStoreRequest $questionStoreRequest 117 * @param Question $question 118 * @return \Illuminate\Http\RedirectResponse 119 */ 120 public function update(QuestionStoreRequest $questionStoreRequest, Question $question) 121 { 122 //更新前 判断下权限 123 if (!(auth()->user()->can('update', $question))) { 124 //返回警告 没有权限 125 return redirect()->back()->with('warning', '你不能编辑不属于你的问题!'); 126 } 127 //取得更新的字段 使用Eloquent提供的update方法执行问题更新 128 $question->update([ 129 'title' => $questionStoreRequest->get('title'), 130 'content' => $questionStoreRequest->get('content'), 131 ]); 132 133 134 //topics的操作这时候看起来有点臃肿 可以使用TopicController来管理,暂时省略 135 //存储topics 136 $topics = $this->questionRepository->normalizeTopics($questionStoreRequest->get('topics')); 137 //使用我们再question model里面添加的topics方法获得 topics关联, 138 //再使用sync方法同步tag 【删除的会被删除掉,没删除的就保留,新的就增加】 139 $question->topics()->sync($topics); 140 141 //更新完成,跳转回去 142 return redirect()->back(); 143 } 144 145 146 /**Remove the specified resource from storage. 147 * @param Question $question 148 * @return \Illuminate\Http\RedirectResponse 149 * @throws \Exception 150 */ 151 public function destroy(Question $question) 152 { 153 // 154 if (auth()->user()->can('destroy', $question)) { 155 $question->delete(); 156 return redirect()->route('questions.index')->with('success', "删除成功!"); 157 } 158 return redirect()->back()->with('danger', "你不能删除不属于你的问题!"); 159 } 160 161 162 } 163
AnswerController.php:
1 <?php 2 3 namespace App\Http\Controllers; 4 5 use App\Answer; 6 use App\Models\Question; 7 use Illuminate\Http\Request; 8 9 class AnswerController extends Controller 10 { 11 12 /** 13 * AnswerController constructor. 14 */ 15 public function __construct() 16 { 17 //只有登录用户才能回答问题 但是可以查看 和查看提交创建页面,引导用户去注册 18 $this->middleware(['auth',])->except(['index', 'create', 'show']); 19 } 20 21 /** 22 * Display a listing of the resource. 23 * 24 * @return \Illuminate\Http\Response 25 */ 26 public function index() 27 { 28 // 29 } 30 31 /** 32 * Show the form for creating a new resource. 33 * 34 * @return \Illuminate\Http\Response 35 */ 36 public function create() 37 { 38 // 39 } 40 41 42 /** Store a newly created resource in storage. 43 * @param Request $request 44 * @param Question $question 45 * @return \Illuminate\Http\RedirectResponse 46 */ 47 public function store(Request $request, Question $question) 48 { 49 // 50 51 52 //初始化回答数据 53 $data = $request->validate([ 54 'content' => 'required|min:30', 55 ]); 56 $data['user_id'] = auth()->user()->id; 57 58 $data['question_id'] = $question->id; 59 60 //创建回答入库 61 $answer = Answer::create($data); 62 //更新问题里的数据 63 $question->increment('answers_count'); 64 //更新用户里的数据 65 auth()->user()->increment('answers_count'); 66 return redirect()->back()->with('success', "回答成功!"); 67 } 68 69 /** 70 * Display the specified resource. 71 * 72 * @param \App\Answer $answer 73 * @return \Illuminate\Http\Response 74 */ 75 public function show(Answer $answer) 76 { 77 // 78 } 79 80 /** 81 * Show the form for editing the specified resource. 82 * 83 * @param \App\Answer $answer 84 * @return \Illuminate\Http\Response 85 */ 86 public function edit(Answer $answer) 87 { 88 // 89 } 90 91 /** 92 * Update the specified resource in storage. 93 * 94 * @param \Illuminate\Http\Request $request 95 * @param \App\Answer $answer 96 * @return \Illuminate\Http\Response 97 */ 98 public function update(Request $request, Answer $answer) 99 { 100 // 101 } 102 103 /** 104 * Remove the specified resource from storage. 105 * 106 * @param \App\Answer $answer 107 * @return \Illuminate\Http\Response 108 */ 109 public function destroy(Answer $answer) 110 { 111 // 112 } 113 } 114
Answer.php
1 <?php 2 3 namespace App; 4 5 use App\Models\Question; 6 use Illuminate\Database\Eloquent\Model; 7 use Illuminate\Database\Eloquent\SoftDeletes; 8 9 class Answer extends Model 10 { 11 #region 支持软删除添加 12 use SoftDeletes; 13 protected $dates = ['deleted_at']; 14 15 #endregion 16 17 protected $fillable = ['user_id', 'question_id', 'content']; 18 19 /** 一个回答只有一个回答主人 20 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 21 */ 22 public function user() 23 { 24 return $this->belongsTo(User::class); 25 } 26 27 /** 一个回答只针对一个问题 28 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 29 */ 30 public function question() 31 { 32 return $this->belongsTo(Question::class); 33 } 34 35 } 36