Laravel+Vue 问答平台项目实战API接口 - 问题相关接口

601 阅读3分钟

目录

  • 实现问题列表接口
  • 实现查看问题详情接口
  • 实现提交问题接口
  • 实现喜欢问题接口
  • 实现取消喜欢接口
  • 实现回答问题接口

设计路由

实现目标中的接口,其中获取问题列表数据以及查看问题详情接口是开放性的,与当前用户是否登录无关,所以不需要认证中间件保护,而提交问题,喜欢问题,取消喜欢问题以及回答问题接口是与当前操作人的用户属性相关的,所以路由需要认证中间件保护,仅登录状态下的用户才可以操作

编辑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' => [],
        ];
    }
}
问题分页列表接口

通过接口参数中的pagesize字段,分页获取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);
    }
}
提交问题接口

保存用户提交的问题数据即可