Recommended reading

Build table

As mentioned in the next article, the compilation of recommended reading is realized through collection.

As usual, look at the project design draft first, and then design the table structure.

 Schema::create('collections'.function (Blueprint $table) {
     $table->increments('id');
     $table->string('name');
     $table->string('avatar');
     $table->string('description');

     $table->unsignedInteger('post_count')->default(0);
     $table->unsignedInteger('fans_count')->default(0);

     $table->unsignedInteger('user_id')->comment('Creator');

     $table->timestamps();
 });
Copy the code

The topic exists as collection_admin, collection_author, collection_follower, and collection_post For example, collection_POST is an intermediate table. It is collection and POST.

Schema::create('collection_post'.function (Blueprint $table) {
    $table->unsignedInteger('post_id');
    $table->unsignedInteger('collection_id');

    $table->timestamp('passed_at')->nullable()->comment('Approval time');

    $table->timestamps();

    $table->index('post_id');
    $table->index('collection_id');

    $table->unique(['post_id'.'collection_id']);
});
Copy the code

After you’ve created the table, fill in the Seeder.

modeling

# Collection.php


      

namespace App\Models;

class Collection extends Model
{
    public function posts(a)
    {
        return $this->belongsToMany(Post::class, 'collection_post'); }}Copy the code
# Post.php


      

namespace App\Models;

class Post extends Model
{
	// ...

    public function collections(a)
    {
        return $this->belongsToMany(Collection::class, 'collection_post'); }}Copy the code

With the Collection, you can now implement the last part of your post detail page design

Project income

The first is the feature section, where RESTful specifications allow us to design an API like this

Test.com/api/posts/ {… , here the coding is relatively simple, refer to the source code

Recommended reading

The first step is to design the API according to the RESTful specification

Test.com/api/posts/ {…

Corresponding controller code

# PostController.php

public function indexOfRecommend($post)
{
    $collectionIds = $post->collections()->pluck('id');

    $query = Post::whereHas('collections'.function ($query) use ($collectionIds) {
        $query->whereIn('collection_id', $collectionIds);
    });

    // Sort problems
    $posts = $query->columns()->paginate();

    return PostResource::make($posts);
}
Copy the code

As a caveat, the whereHas provided by Laravel generates an inefficient SQL statement that loads the full table. But the purpose of the series is to write descriptive RESTful apis, so no further optimization will be done here.

Observer

The Observer is both an Observer and can be used for code decoupling to keep the controller simple. The next two pieces of logic cover Observer usage scenarios.

The heat

$posts = $query->columns()->paginate(); If order Derby is not specified in this statement, MySQL will fetch the posts in order of ID, ASC, but in general community sites, there will usually be a heat, and then fetch the posts in order of heat.

This part of the sorting algorithm and many, according to the product given formula calculation can be

The following assumes that heat = a * (timestamp – 1546300800) + B * read_count + C * like_count

A/B/C represents the weight of each feature, which can be adjusted at any time according to operational requirements. Because the timestamp is too large, the timestamp number is reduced by subtractingthe timestamp 1546300800 of 2019-01-01. Of course, even so, a will still be a large number, so the value of A will be very small

Schema::create('posts'.function (Blueprint $table) {
	// ...
    
    $table->integer('heat')->index()->comment('heat');
    
	// ...
});
Copy the code

Since the project is in development, we modify the original Migration directly and add the HEAT field. Then perform

> php artisan migrate:refresh --seed

Observe observe observe observe Observe Observe Observe Observe Observe Observe Observe Observe Observe Observe Observe Observe Observe observe observe observe observe observe observe observe observe observe observe observe observe

Once the observer is created from the document and registered, you can write the relevant code

> php artisan make:observer PostObserver --model=Models/Post

class PostObserver
{
    / * * *@param Post $post
     */
    public function saving(Post $post)
    {
        if ($post->isDirty(['like_count'.'read_count'])) {
            $heat = 0.001 * ($post->created_at->timestamp - 1546300800)
                + 10 * $post->read_count
                + 1000* $post->like_count; $post->heat = (integer)$heat; }}}Copy the code

Calls to $model->save/update/create trigger the saving method before persisting to the database.

Create a comment

Basic coding


      

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Comment;
use App\Resources\CommentResource;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class CommentController extends Controller
{
    / * * *@param  \Illuminate\Http\Request $request
     * @return \Illuminate\Contracts\Routing\ResponseFactory|Response
     */
    public function store(Request $request)
    {
        $data = $request->all();

        $data['user_id'] = \Auth::id();
        $data['floor'] = Comment::where('post_id', $request->input('post_id'))->max('floor') + 1;
        $comment = Comment::create($data);

        // In RESTful specifications, a 201 status code is returned after a successful creation
        return \response(CommentResource::make($comment), 201); }}Copy the code

Model


      

namespace App\Models;

use Staudenmeir\EloquentEagerLimit\HasEagerLimit;

class Comment extends Model
{
    use HasEagerLimit;

    protected $fillable = ['content'.'user_id'.'post_id'.'floor'.'selected'];

    public function getLikeCountAttribute(a)
    {
        return $this->attributes['like_count']????0;
    }

    public function getReplyCountAttribute(a)
    {
        return $this->attributes['reply_count']????0;
    }
Copy the code

Because the create method is used, you need to declare $fillable in the model

Because the default values for LIKE_count and reply_count are 0, create does not set LIKE_count, reply_count. But that would leave $comment in the controller’s store method without the like_count and reply_count keys, which is very front-end unfriendly. For example in VUE the usual approach here is this.ments.push (comment). There are two ways to solve this problem

  • $data[‘like_count’] = 0 and $data[‘reply_count’] = 0

  • Set the default values for both keys using the model modifier (illustrated in the Comment model above)

Using either of these methods ensures that the query is consistent with the data at creation time.

API presentation, corresponding Postman documentation attached at the end of the article

In the controller code, the corresponding Model is assigned to tree-QL processing, so you can still use include to ensure the corresponding data consistency.

There is a redundancy of comment_count in the posts table, so when creating a comment, you also need the corresponding post.ment_count + 1. Create and register CommentObserver. Then complete the coding

# CommentObserver.php


      

namespace App\Observers;

use App\Models\Comment;

class CommentObserver
{
    public function created(Comment $comment)
    {
        $comment->post()->increment('comment_count'); }}Copy the code

supplement

Post release process

A possible problem is that when a user wants to modify an already published post again, if the half-modified post triggers the automatic saving mechanism, the half-modified post will be displayed on the home page, etc.

So instead of a Posts table, you need to add a single table of drafts to serve as a draft box, where users create and modify their drafts, except when they click on a post, to synchronize the corresponding drafts to the Posts table. The relevant process can refer to the brief book.

Publish process coding sample

# DraftController.php

public function published(Draft $draft)
{
    Validator::make($draft->getAttributes(), [
        'title'= >'required|max:255'.'content'= >'required'
    ])->validate();

    $draft->published();

    return response(null.201);
}
Copy the code
public function published(a)
{
    if (!$this->post_id) {
        $post = Post::create([
            'user_id'= >$this->user_id,
            'title'= >$this->title,
            'content'= >$this->content,
            'published_at'= >$this->freshTimestampString(),
        ]);

        $this->post_id = $post->id;
        $this->save();
    } else {
        $post = Post::findOrFail($this->post_id);
        $post->title = $this->title;
        $post->content = $this->content; $post->save(); }}Copy the code

Refer to the source code for the rest and the Postman documentation for the API.

related

  • Api Document documenter.getpostman.com/view/150062…
  • Telescope, an online debugging tool
  • This section source weiwenhao/ community-API
  • Api Tool weiwenhao/tree-ql
  • The previous section wrote a descriptive RESTful API (I): Workflow