目录
- 实现问题列表接口
- 实现查看问题详情接口
- 实现提交问题接口
- 实现喜欢问题接口
- 实现取消喜欢接口
- 实现回答问题接口
设计路由
实现目标中的接口,其中获取问题列表数据以及查看问题详情接口是开放性的,与当前用户是否登录无关,所以不需要认证中间件保护,而提交问题,喜欢问题,取消喜欢问题以及回答问题接口是与当前操作人的用户属性相关的,所以路由需要认证中间件保护,仅登录状态下的用户才可以操作
编辑routes/api.php文件后,内容如下
<?php
Route::post('auth/login', 'AuthController@login');
Route::post('auth/register', 'AuthController@register');
// 需要认证,才可以访问
Route::group(['middleware' => 'auth:api'], function () {
Route::post('user/logout', 'AuthController@logout');
Route::get('user/info', 'UserController@find');
Route::post('question/like', 'QuestionController@like');
Route::post('question/like/cancel', 'QuestionController@cancelLike');
Route::post('question/answer', 'QuestionController@saveAnswer');
Route::post('question/create', 'QuestionController@create');
});
// 问题列表分页数据
Route::get('question/paginate', 'QuestionController@paginate');
Route::get('question/show', 'QuestionController@show');
构建QuestionController控制器
在项目根目录下执行命令
php artisan make:controller Api/QuestionController,此命令会创建app/Http/Controllers/Api/QuestionController控制器,编辑内容如下
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\CreateQuestionRequest;
use App\Http\Resources\Question as ResourcesQuestion;
use App\Models\AnswerQuestion;
use App\Models\LikeQuestion;
use App\Models\Question;
use Illuminate\Http\Request;
class QuestionController extends Controller
{
/**
* 分页获取问题列表.
*/
public function paginate(Request $request)
{
$params = $request->all();
$page = $params['page'] ?? 1;
$size = $params['size'] ?? 10;
$skipCount = ($page - 1) * $size;
$questions = Question::orderBy('created_at', 'desc')
->skip($skipCount)
->take($size)
->get();
return [
'code' => 0,
'msg' => 'success',
'data' => [
'list' => ResourcesQuestion::collection($questions),
'page' => ++$page,
'size' => $size,
],
];
}
/**
* 查看问题详情.
*/
public function show(Request $request)
{
$this->validate($request, [
'id' => 'required',
], [
'id.required' => 'id字段不能为空',
]);
$id = $request->all()['id'];
$question = Question::find($id);
return [
'code' => 0,
'msg' => 'success',
'data' => [
'question' => new ResourcesQuestion($question),
],
];
}
/**
* 喜欢问题.
*/
public function like(Request $request)
{
$this->validate($request, [
'id' => 'required',
], [
'id.required' => 'id字段不能为空',
]);
$id = $request->all()['id'];
$question = Question::find($id);
if (!$question) {
throw new \Exception('问题不存在');
}
$user = auth()->user();
$likeQuestion = LikeQuestion::where('user_id', $user->id)
->where('question_id', $id)
->first();
if (!$likeQuestion) {
LikeQuestion::create([
'user_id' => $user->id,
'question_id' => $id,
]);
}
return [
'code' => 0,
'msg' => 'success',
'data' => [],
];
}
/**
* 取消喜欢问题.
*/
public function cancelLike(Request $request)
{
$this->validate($request, [
'id' => 'required',
], [
'id.required' => 'id字段不能为空',
]);
$id = $request->all()['id'];
$question = Question::find($id);
if (!$question) {
throw new \Exception('问题不存在');
}
$user = auth()->user();
$likeQuestion = LikeQuestion::where('user_id', $user->id)
->where('question_id', $id)
->first();
if ($likeQuestion) {
$likeQuestion->delete();
}
return [
'code' => 0,
'msg' => 'success',
'data' => [],
];
}
/**
* 回答问题.
*/
public function saveAnswer(Request $request)
{
$this->validate($request, [
'id' => 'required',
'content' => 'required',
], [
'id.required' => 'id字段不能为空',
'content.required' => '回答问题内容不能为空',
]);
$id = $request->all()['id'];
$content = $request->all()['content'];
$question = Question::find($id);
if (!$question) {
throw new \Exception('问题不存在');
}
$user = auth()->user();
AnswerQuestion::create([
'user_id' => $user->id,
'question_id' => $id,
'content' => $content,
]);
return [
'code' => 0,
'msg' => 'success',
'data' => [],
];
}
/**
* 创建问题.
*/
public function create(CreateQuestionRequest $request)
{
$data = $request->all();
$user = auth()->user();
Question::create([
'title' => $data['title'],
'content' => $data['content'],
'user_id' => $user->id,
]);
return [
'code' => 0,
'msg' => 'success',
'data' => [],
];
}
}
问题分页列表接口
通过接口参数中的page和size字段,分页获取questions表中的数据。接口返回的数据中list字段对应的数数据经过App\Http\Resources\Question as ResourcesQuestion;资源类处理后返回。
在项目根目录下执行artisan命令 php artisan make:resource Question,编辑如下代码
<?php
namespace App\Http\Resources;
use App\Models\LikeQuestion;
use Illuminate\Http\Resources\Json\JsonResource;
class Question extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
*
* @return array
*/
public function toArray($request)
{
$user = auth()->user();
$hasLike = 0;
if ($user) {
$likeQuestion = LikeQuestion::where('user_id', $user->id)
->where('question_id', $this->id)
->first();
if ($likeQuestion) {
$hasLike = 1;
}
}
return [
'id' => $this->id,
'user_id' => $this->user_id,
'title' => $this->title,
'content' => $this->content,
'author' => 'AntFoot',
'comment_num' => $this->comment_num,
'like_num' => $this->like_num,
'has_like' => $hasLike,
'answers' => Answer::collection($this->answers),
'created_at' => $this->created_at->format('Y.m.d'),
];
}
}
hasLike字段是标记当前登录用户,是否已经喜欢此问题
$user = auth()->user();
$hasLike = 0;
if ($user) {
$likeQuestion = LikeQuestion::where('user_id', $user->id)
->where('question_id', $this->id)
->first();
if ($likeQuestion) {
$hasLike = 1;
}
}
如果当前请求中,用户已登录,则查询like_questions表,是否存在此用户的喜欢记录,如果存在则表示已喜欢。
'answers' => Answer::collection($this->answers),是嵌套的此问题对应的回答数据结构。
Answer是回答问题的资源类,在项目根目录下执行命令php artisan make:resource Answer,编辑如下代码
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Answer extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
*
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'user_id' => $this->user_id,
'user_name' => 'AntFoot',
'question_id' => $this->question_id,
'content' => $this->content,
'created_at' => $this->created_at->format('Y.m.d H:i'),
];
}
}
查看问题详情接口
通过接口参数id查看问题数据,通过ResourcesQuestion 资源类格式化后返回数据
喜欢问题接口
通过接口参数id查看问题数据,然后读取当前登录用户信息,再获取此用户的喜欢问题记录,如果不存在,则创建喜欢记录。
由于questions表有一个字段like_num用来统计该问题被喜欢的数量。所以当有用户喜欢某个问题时,应该更新该问题记录中的like_num字段值,增1。而控制器中的like方法中,并没有执行此操作,而是通过ORM中的模型观察者模式来实现。
使用artisan命令 make:observer 创建一个对LikeQuestion模型的LikeQuestionObserver观察者类
php artisan make:observer LikeQuestionObserver --model=Models/LikeQuestion
此命令会将新的观察者生成到 App/Observers 目录,如果这个目录不存在,会自动创建。
编辑created函数
public function created(LikeQuestion $likeQuestion)
{
$questionId = $likeQuestion->question_id;
Question::find($questionId)->increment('like_num');
}
当LikeQuestion模型创建记录时执行created函数,然后给Question模型对应的id记录的like_num值增1
编辑deleted函数
public function deleted(LikeQuestion $likeQuestion)
{
$questionId = $likeQuestion->question_id;
Question::find($questionId)->decrement('like_num');
}
当LikeQuestion模型软删除时执行deleted函数,然后给Question模型对应的id记录的like_num值减1。此函数是在用户取消喜欢问题时会调用。
修改后的观察者类代码如下:
<?php
namespace App\Observers;
use App\Models\LikeQuestion;
use App\Models\Question;
class LikeQuestionObserver
{
/**
* Handle the like question "created" event.
*
* @return void
*/
public function created(LikeQuestion $likeQuestion)
{
$questionId = $likeQuestion->question_id;
Question::find($questionId)->increment('like_num');
}
/**
* Handle the like question "updated" event.
*
* @return void
*/
public function updated(LikeQuestion $likeQuestion)
{
}
/**
* Handle the like question "deleted" event.
*
* @return void
*/
public function deleted(LikeQuestion $likeQuestion)
{
$questionId = $likeQuestion->question_id;
Question::find($questionId)->decrement('like_num');
}
/**
* Handle the like question "restored" event.
*
* @return void
*/
public function restored(LikeQuestion $likeQuestion)
{
}
/**
* Handle the like question "force deleted" event.
*
* @return void
*/
public function forceDeleted(LikeQuestion $likeQuestion)
{
}
}
然后,修改app/Providers/AppServiceProvider.php文件,在boot函数中注册观察者,修改后如下
<?php
namespace App\Providers;
use App\Models\LikeQuestion;
use App\Observers\LikeQuestionObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
LikeQuestion::observe(LikeQuestionObserver::class);
}
}
取消喜欢问题接口
通过接口参数id查看问题数据,然后读取当前登录用户信息,再获取此用户的喜欢问题记录,如果存在,则进行软删除。
此时根据LikeQuestionObserver观察者中实现的deleted方法完成。
保存回答接口
接口参数有问题id,和回答内容两项。然后读取当前用户信息,写入answer_questions表记录。新增回答问题记录之后,同样,需要更新questions表中的comment_num字段,此字段用来统计问题的回答数量。这块功能也是通过ORM中的模型观察者模式来实现。
使用artisan命令 make:observer 创建一个对AnswerQuestion模型的AnswerQuestionObserver观察者类
php artisan make:observer AnswerQuestionObserver --model=Models/AnswerQuestion
编辑app/Observers/AnswerQuestionObserver观察者类的created函数
/**
* Handle the answer question "created" event.
*
* @return void
*/
public function created(AnswerQuestion $answerQuestion)
{
$questionId = $answerQuestion->question_id;
Question::find($questionId)->increment('comment_num');
}
当AnswerQuestion模型创建记录时执行created函数,然后给Question模型对应的id记录的comment_num字段值增1
修改文件app/Providers/AppServiceProvider文件,在boot函数中,注册AnswerQuestionObserver
<?php
namespace App\Providers;
use App\Models\AnswerQuestion;
use App\Models\LikeQuestion;
use App\Observers\AnswerQuestionObserver;
use App\Observers\LikeQuestionObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
LikeQuestion::observe(LikeQuestionObserver::class);
AnswerQuestion::observe(AnswerQuestionObserver::class);
}
}
提交问题接口
保存用户提交的问题数据即可