Laravel and angular cheatsheet

Laravel and angular cheatsheet

Mount linux file system under Macos:

sudo fuse-ext2 /dev/disk3s1 /mnt/disk2s1 -o force

Laravel frequently used references:

https://phpdocker.io/generator

https://laravel.com/docs/5.8/queries
https://laravel.com/docs/5.8/eloquent

https://blog.csdn.net/chenrui310/article/details/80664418  ORM methods

https://laravel.com/docs/master/helpers
https://github.com/Metabuyer/laravel-permission-mongodb
https://github.com/designmynight/laravel-mongodb-passport
https://github.com/jenssegers/laravel-mongodb
http://andersonandra.de/l5-repository
https://laravel.com/docs/5.8/passport

Illuminate\Support\Str  : https://learnku.com/articles/16807

contracts vs facades list: 
https://learnku.com/docs/laravel/5.5/contracts/1292
In 'Illuminate/Foundation/Application.php':registerCoreContainerAliases()
    https://segmentfault.com/a/1190000004946198

Facades reference list:
https://learnku.com/docs/laravel/5.5/facades/1291
ServiceProvider binding:
https://learnku.com/docs/laravel/5.5/container/1289

Vue.js nuxt:  https://zh.nuxtjs.org/guide/installation

Laravel-mix webpack.mix.js example:

require('dotenv').config();
let mix = require('laravel-mix');
mix
  .js('resources/assets/js/app.js', 'public/js')
  .styles([
    'node_modules/open-sans-all/css/open-sans.css',
    'node_modules/font-awesome/css/font-awesome.css',
    'resources/assets/css/style.css'
  ], 'public/css/style.css')
  .copy('node_modules/open-sans-all/fonts',  'public/fonts')
  .copy('node_modules/font-awesome/fonts',  'public/fonts')
  .copy('resources/assets/images', 'public/images')
  .browserSync({
    proxy: process.env.APP_URL,
    open: false
  })
;
// config webpack to extract vue.js SFCs' CSS to public/css/vue-style.css:
mix.options({
  extractVueStyles: 'public/css/vue-style.css'
});

mix.webpackConfig({
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.runtime.esm.js'
    }
  }
});

package.json example (using Vue.js):

{
  "private": true,
  "scripts": {
    "dev": "npm run development",
    "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --display-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "npm run watch -- --watch-poll",
    "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "prod": "npm run production",
    "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },
  "devDependencies": {
    "axios": "^0.17",
    "browser-sync": "^2.18.13",
    "browser-sync-webpack-plugin": "^1.2.0",
    "core-js": "^2.5.1",
    "cross-env": "^5.1",
    "dotenv": "^4.0.0",
    "font-awesome": "^4.7.0",
    "laravel-mix": "^1.4",
    "open-sans-all": "^0.1.2",
    "vue": "^2.5.3"
  }

Laravel blade template system interpolation V.S. Vue.js interpolation:

Use {!! !!} syntax from Laravel blade template system instead of original {{ }} syntax, so that the following script can work:

  <script type="text/javascript">
    window.vue_listing_model = "{!! addslashes(json_encode($model)) !!}"
  </script>

Content distribution( in Vue.js) as content-child( content projection in Angular), is to solve the following problem, making components more reusable:

VUEX mechanism:

VUEX has a store with lots of states inside it. To change any state in the store, you should use mutators to do so, and mutators are synchronous so that race conditions are avoided when changing the states in the store. And use commit(mutation-name, payload) to invoke the mutation method inside the store to make the state change happen.  If you have state gained from asynchronous resources as from invoking APIs, you have to use actions defined in your Vues.Store, in the action functions use commit(mutation-name, payload) as well to change the state. To invoke actions defined in the store, use this.$store.dispatch(action-name, payload) method.

composer resource:

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

composer config -g --unset repos.packagist

composer config repo.packagist composer https://mirrors.aliyun.com/composer/

composer config --unset repos.packagist

composer -vvv require alibabacloud/sdk

composer create-project laravel/laravel=5.6.* –prefer-dist hokhykblog

composer require barryvdh/laravel-ide-helper –dev

composer require prettus/l5-repository

php artisan vendor:publish --provider "Prettus\Repository\Providers\RepositoryServiceProvider"

composer require league/fractal

Or use resource API classes:
php artisan make:resource User
php artisan make:resource Users --collection
php artisan make:resource UserCollection

TDD:

php artisan make:entity "Blog\Article"

php artisan make:factory ArticleFactory
php artisan make:factory PostFactory --model=Post

Model schema serialization using casts array:

in Model:  protected $casts = ['saved' => 'array'];

in Migration file:  $table->text('saved');

$php artisan tinker
echo gettype($model->saved())
//array

in Seeder file: EloquentModel::create([..., 'saved'=>[1,2,3,4], ...]);
class MyTest extends PHPUnit_Framework_TestCase{
    /**
     * @group specification
     */
    public function testSomething(){
    }
phpunit  --group specification

php artisan make:test NewPostTest
php artisan make:test NewPostTest --unit


    public function testUserLoginsSuccessfully()
    {
        $user = factory(User::class)->create([
            'email' => '[email protected]',
            'password' => bcrypt('toptal123'),
        ]);

        $payload = ['email' => '[email protected]', 'password' => 'toptal123'];

        $this->json('POST', 'api/login', $payload)
            ->assertStatus(200)
            ->assertJsonStructure([
                'data' => [
                    'id',
                    'name',
                    'email',
                    'created_at',
                    'updated_at',
                    'api_token',
                ],
            ]);

    }

    public function testsRegistersSuccessfully()
    {
        $payload = [
            'name' => 'John',
            'email' => '[email protected]',
            'password' => 'toptal123',
            'password_confirmation' => 'toptal123',
        ];

        $this->json('post', '/api/register', $payload)
            ->assertStatus(201)
            ->assertJsonStructure([
                'data' => [
                    'id',
                    'name',
                    'email',
                    'created_at',
                    'updated_at',
                    'api_token',
                ],
            ]);;
    }

    public function testsRequiresPasswordEmailAndName()
    {
        $this->json('post', '/api/register')
            ->assertStatus(422)
            ->assertJson([
                'name' => ['The name field is required.'],
                'email' => ['The email field is required.'],
                'password' => ['The password field is required.'],
            ]);
    }

    public function testsRequirePasswordConfirmation()
    {
        $payload = [
            'name' => 'John',
            'email' => '[email protected]',
            'password' => 'toptal123',
        ];

        $this->json('post', '/api/register', $payload)
            ->assertStatus(422)
            ->assertJson([
                'password' => ['The password confirmation does not match.'],
            ]);
    }

    public function testUserIsLoggedOutProperly()
    {
        $user = factory(User::class)->create(['email' => '[email protected]']);
        $token = $user->generateToken();
        $headers = ['Authorization' => "Bearer $token"];

        $this->json('get', '/api/articles', [], $headers)->assertStatus(200);
        $this->json('post', '/api/logout', [], $headers)->assertStatus(200);

        $user = User::find($user->id);

        $this->assertEquals(null, $user->api_token);
    }

    public function testUserWithNullToken()
    {
        // Simulating login
        $user = factory(User::class)->create(['email' => '[email protected]']);
        $token = $user->generateToken();
        $headers = ['Authorization' => "Bearer $token"];

        // Simulating logout
        $user->api_token = null;
        $user->save();

        $this->json('get', '/api/articles', [], $headers)->assertStatus(401);
    }

    public function testsArticlesAreCreatedCorrectly()
    {
        $user = factory(User::class)->create();
        $token = $user->generateToken();
        $headers = ['Authorization' => "Bearer $token"];
        $payload = [
            'title' => 'Lorem',
            'body' => 'Ipsum',
        ];

        $this->json('POST', '/api/articles', $payload, $headers)
            ->assertStatus(200)
            ->assertJson(['id' => 1, 'title' => 'Lorem', 'body' => 'Ipsum']);
    }

    public function testsArticlesAreUpdatedCorrectly()
    {
        $user = factory(User::class)->create();
        $token = $user->generateToken();
        $headers = ['Authorization' => "Bearer $token"];
        $article = factory(Article::class)->create([
            'title' => 'First Article',
            'body' => 'First Body',
        ]);

        $payload = [
            'title' => 'Lorem',
            'body' => 'Ipsum',
        ];

        $response = $this->json('PUT', '/api/articles/' . $article->id, $payload, $headers)
            ->assertStatus(200)
            ->assertJson([ 
                'id' => 1, 
                'title' => 'Lorem', 
                'body' => 'Ipsum' 
            ]);
    }

    public function testsArtilcesAreDeletedCorrectly()
    {
        $user = factory(User::class)->create();
        $token = $user->generateToken();
        $headers = ['Authorization' => "Bearer $token"];
        $article = factory(Article::class)->create([
            'title' => 'First Article',
            'body' => 'First Body',
        ]);

        $this->json('DELETE', '/api/articles/' . $article->id, [], $headers)
            ->assertStatus(204);
    }

    public function testArticlesAreListedCorrectly()
    {
        factory(Article::class)->create([
            'title' => 'First Article',
            'body' => 'First Body'
        ]);

        factory(Article::class)->create([
            'title' => 'Second Article',
            'body' => 'Second Body'
        ]);

        $user = factory(User::class)->create();
        $token = $user->generateToken();
        $headers = ['Authorization' => "Bearer $token"];

        $response = $this->json('GET', '/api/articles', [], $headers)
            ->assertStatus(200)
            ->assertJson([
                [ 'title' => 'First Article', 'body' => 'First Body' ],
                [ 'title' => 'Second Article', 'body' => 'Second Body' ]
            ])
            ->assertJsonStructure([
                '*' => ['id', 'body', 'title', 'created_at', 'updated_at'],
            ]);
    }

public function testInvalidPostID() {
    $this->json('PUT', '/posts/2222/sh-comments/1001', [
        'input' => 'SomeThing'
            ], [
        'Authorization' => 'Bearer ' . app('jwt')->getTokenForUser(2)
    ])->dump() //dumpnig output as array
      ->seeJsonStructure(['errors' => [
            '*' => [
                'message'
            ]
        ]
    ])->assertResponseStatus(404);
}

php artisan make:Model Article -m (--migration)
php artisan make:migration --table=users adds_api_token_to_users_table
php artisan migrate --step

php artisan make:seeder ArticlesTableSeeder
php artisan db:seed --class=ArticlesTableSeeder

php artisan make:controller ArticleController


Route::get('articles', 'ArticleController@index');
Route::get('articles/{id}', 'ArticleController@show');
Route::post('articles', 'ArticleController@store');
Route::put('articles/{id}', 'ArticleController@update');
Route::delete('articles/{id}', 'ArticleController@delete');


    public function updateArticleCategory(UpdateArticleCategoryRequest $request)
    {
        $articleCategory = $this->call(UpdateArticleCategoryAction::class, [$request]);
        $data = $this->transform($articleCategory, ArticleCategoryTransformer::class);

        return $this->json(['code' => Response::HTTP_OK, 'message' => '编辑成功', 'result' => $data]);
    }

    public function deleteArticleCategory(DeleteArticleCategoryRequest $request)
    {
        $this->call(DeleteArticleCategoryAction::class, [$request]);

        return $this->json(['code' => Response::HTTP_OK, 'message' => '删除成功']);
    }

Passport Oauth2:

php artisan passport:client --password

$http = new GuzzleHttp\Client;

$response = $http->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'password',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'username' => '[email protected]',
        'password' => 'my-password',
        'scope' => '',
    ],
]);

return json_decode((string) $response->getBody(), true);

public function testServerCreation()
{
    Passport::actingAs(
        factory(User::class)->create(),
        ['create-servers']
    );

    $response = $this->post('/api/create-server');

    $response->assertStatus(200);
}

/**
 * The event listener mappings for the application.
 *
 * @var array
 */
protected $listen = [
    'Laravel\Passport\Events\AccessTokenCreated' => [
        'App\Listeners\RevokeOldTokens',
    ],

    'Laravel\Passport\Events\RefreshTokenCreated' => [
        'App\Listeners\PruneOldTokens',
    ],
];

use Illuminate\Http\Request;

Route::get('/orders', function (Request $request) {
    if ($request->user()->tokenCan('place-orders')) {
        //
    }
});

检查作用域
Passport 提供两个可用于验证输入请求是否经过已发放作用域的令牌认证的中间件。开始之前,添加下面的中间件到 app/Http/Kernel.php 文件的 $routeMiddleware 属性:

'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,

Git commands:

git init
git remote add ....
git push --set-upstream origin master

git checkout -b dev
git push origin dev:dev  //git push origin :dev --to delete the branch
git push --set-upstream origin dev

git checkout --track  origin/dev  //create local dev branch to track existing remote branch  or use the following:
git  checkout -b dev  origin/dev

1.owner在远程库更新了分支,但是在我这里git branch -a查看不到新的分支,

  解决办法:git fetch origin --prune 更新一下分支信息,然后再git branch -a就能看到新的分支了~
2.多人协作的工作模式通常是这样:

首先,可以试图用git push origin <branch-name>推送自己的修改;

如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;

如果合并有冲突,则解决冲突,并在本地提交;

没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!

   5. 如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>。

php artisan make:controller API/AuthController

php artisan make:resource ModelsResource

ARTISAN:

Available commands:
  clear-compiled      Remove the compiled class file
  down                Put the application into maintenance mode
  env                 Display the current framework environment
  help                Displays help for a command
  inspire             Display an inspiring quote
  list                Lists commands
  migrate             Run the database migrations
  optimize            Optimize the framework for better performance
  serve               Serve the application on the PHP development server
  tinker              Interact with your application
  up                  Bring the application out of maintenance mode
 app
  app:name            Set the application namespace
 auth
  auth:clear-resets   Flush expired password reset tokens
 cache
  cache:clear         Flush the application cache
  cache:table         Create a migration for the cache database table
 config
  config:cache        Create a cache file for faster configuration loading
  config:clear        Remove the configuration cache file
 db
  db:seed             Seed the database with records
 event
  event:generate      Generate the missing events and listeners based on registration
 handler
  handler:command     Create a new command handler class
  handler:event       Create a new event handler class
 key
  key:generate        Set the application key
 make
  make:command        Create a new command class
  make:console        Create a new Artisan command
  make:controller     Create a new resource controller class
  make:event          Create a new event class
  make:job            Create a new job class
  make:listener       Create a new event listener class
  make:middleware     Create a new middleware class
  make:migration      Create a new migration file
  make:model          Create a new Eloquent model class
  make:policy         Create a new policy class
  make:provider       Create a new service provider class
  make:request        Create a new form request class
  make:seeder         Create a new seeder class
  make:test           Create a new test class
 migrate
  migrate:install     Create the migration repository
  migrate:refresh     Reset and re-run all migrations
  migrate:reset       Rollback all database migrations
  migrate:rollback    Rollback the last database migration
  migrate:status      Show the status of each migration
 queue
  queue:failed        List all of the failed queue jobs
  queue:failed-table  Create a migration for the failed queue jobs database table
  queue:flush         Flush all of the failed queue jobs
  queue:forget        Delete a failed queue job
  queue:listen        Listen to a given queue
  queue:restart       Restart queue worker daemons after their current job
  queue:retry         Retry a failed queue job
  queue:subscribe     Subscribe a URL to an Iron.io push queue
  queue:table         Create a migration for the queue jobs database table
  queue:work          Process the next job on a queue
 route
  route:cache         Create a route cache file for faster route registration
  route:clear         Remove the route cache file
  route:list          List all registered routes
 schedule
  schedule:run        Run the scheduled commands
 session
  session:table       Create a migration for the session database table
 vendor
  vendor:publish      Publish any publishable assets from vendor packages
 view
  view:clear          Clear all compiled view files
npm install --registry=https://registry.npm.taobao.org

npm install –save-dev @angular/cli@latest

npm uninstall bootstrap-sass jquery lodash –save-dev

ng new Client --style=scss --routing   

ng add @angular/pwa

ng generate module pages/home –routing

ng generate component pages/home

ng generate interface interface/user

ng g s services/dataService –module=app

ng g service pages/auth/_services/auth

ng g service shared/_services/http-interceptor

ng g class pages/auth/user

ng g pipe sex-reform

ng build –prod

ng build –prod –href-base /url in browsser

$ng g library zero –prefix hok

$ npm install --save @ngrx/effects @ngrx/store
$ npm install --save-dev @ngrx/schematics

$ npm install –save-dev @ngrx/store-devtools

$ ng generate @ngrx/schematics:store State --root --module app.module.ts

$ ng g @ngrx/schematics:reducer ZipCodes –group

$ ng g @ngrx/schematics:action Zipcode –group

$ ng g @ngrx/schematics:effect Zipcode –group

4 different route guards:

CanActivate, CanActivateChild, CanDeactivate, CanLoad

$ ng g guard pages/auth/_guards/auth

$ npm install –save-dev @ng-bootstrap/schematics

$npm install –save @ng-bootstrap/[email protected] for angular8

To integrate bootstrap scss code into src/styles.scss:

 @import "../node_modules/bootstrap/scss/functions";
 @import "../scss/bootstrap/_variables.scss";
 @import "../node_modules/bootstrap/scss/_variables.scss";
 @import "../node_modules/bootstrap/scss/mixins";
 @import "../node_modules/bootstrap/scss/root";
 @import "../node_modules/bootstrap/scss/reboot";
 @import "../node_modules/bootstrap/scss/type";
 @import "../node_modules/bootstrap/scss/images";
 @import "../node_modules/bootstrap/scss/code";
 @import "../node_modules/bootstrap/scss/grid";
 @import "../node_modules/bootstrap/scss/tables";
 @import "../node_modules/bootstrap/scss/forms";
 @import "../node_modules/bootstrap/scss/buttons";
 @import "../node_modules/bootstrap/scss/transitions";
 @import "../node_modules/bootstrap/scss/dropdown";
 @import "../node_modules/bootstrap/scss/button-group";
 @import "../node_modules/bootstrap/scss/input-group";
 @import "../node_modules/bootstrap/scss/custom-forms";
 @import "../node_modules/bootstrap/scss/nav";
 @import "../node_modules/bootstrap/scss/navbar";
 @import "../node_modules/bootstrap/scss/card";
 @import "../node_modules/bootstrap/scss/breadcrumb";
 @import "../node_modules/bootstrap/scss/pagination";
 @import "../node_modules/bootstrap/scss/badge";
 @import "../node_modules/bootstrap/scss/jumbotron";
 @import "../node_modules/bootstrap/scss/alert";
 @import "../node_modules/bootstrap/scss/progress";
 @import "../node_modules/bootstrap/scss/media";
 @import "../node_modules/bootstrap/scss/list-group";
 @import "../node_modules/bootstrap/scss/close";
 @import "../node_modules/bootstrap/scss/modal";
 @import "../node_modules/bootstrap/scss/tooltip";
 @import "../node_modules/bootstrap/scss/popover";
 @import "../node_modules/bootstrap/scss/carousel";
 @import "../node_modules/bootstrap/scss/utilities";
 @import "../node_modules/bootstrap/scss/print";

Notice that the file import sequence of _variables.scss:

 @import "../scss/bootstrap/_variables.scss";
 @import "../node_modules/bootstrap/scss/_variables.scss";

Because variables in Bootstrap4 are usually defined with !default keyword, thus self-defined variables are imported before Bootstrap variables to override variables’ default values.

Making 3rd party library of your own:

$ng g library zero --prefix hok   //// 创建第三方库 (--prefix: 前缀;在用命令行新建组件/指令时,selector的属性值的前缀)

$cd projects/zero/src/lib // 切换至第三方库创建组件的位置

$ng g c button  //创建 button component

// zero.module.ts
exports: [ButtonComponent]

 
// 切换至第三方包根目录下
$ cd projects/zero
// 构建
$ ng build zero

// 需要登录(npm login),有自己的npm账号
$ npm publish

$ npm install zero@latest --save

imports: [ZeroModule]

<hok-button></hok-buttom>

Stylelint configuration:

npm install stylelint --save-dev 
npm install stylelint-config-standard --save-dev
npm install stylelint-scss --save-dev
// https://github.com/stylelint/stylelint

npm install tslint-angular --save-dev
// https://angular.io/guide/styleguide

In package.json scripts section:

    "lint": "ng lint",
    "lint:dev": "npm run sasslint && npm run tslint",
    "sasslint": "./node_modules/.bin/stylelint \"src/**/*.scss\" --syntax scss || echo \"Ops: Stylelint faild for some file(s).\"",
    "tslint": "./node_modules/.bin/tslint --project tsconfig.json || echo \"Ops: TSlint faild for some file(s).\"",

Copying .stylelintrc file to the root folder of your project:

{
  "extends": ["stylelint-config-standard"],
  "rules": {
    "font-family-name-quotes": "always-where-recommended",
    "function-url-quotes": [
      "always",
      {
        "except": ["empty"]
      }
    ],
    "selector-attribute-quotes": "always",
    "string-quotes": "double",
    "max-nesting-depth": 3,
    "selector-max-compound-selectors": 3,
    "selector-max-specificity": "0,3,2",
    "declaration-no-important": true,
    "at-rule-no-vendor-prefix": true,
    "media-feature-name-no-vendor-prefix": true,
    "property-no-vendor-prefix": true,
    "selector-no-vendor-prefix": true,
    "value-no-vendor-prefix": true,
    "no-empty-source": null,
    "selector-class-pattern": "[a-z-]+",
    "selector-id-pattern": "[a-z-]+",
    "selector-max-id": 0,
    "selector-no-qualifying-type": true,
    "selector-max-universal": 0,
    "selector-pseudo-element-no-unknown": [
      true,
      {
        "ignorePseudoElements": ["ng-deep"]
      }
    ],
    "unit-whitelist": ["px", "%", "em", "rem", "vw", "vh", "deg"],
    "max-empty-lines": 2
  }
}

tslint.json:

{
    "extends": ["../tslint.json", "../node_modules/tslint-angular"],
    "rules": {
        "angular-whitespace": [true, "check-interpolation", "check-semicolon"],
        "no-unused-variable": true,
        "no-unused-css": true,
        "banana-in-box": true,
        "use-view-encapsulation": true,
        "contextual-life-cycle": true,
        "directive-selector": [
            true,
            "attribute",
            "app",
            "camelCase"
        ],
        "component-selector": [
            true,
            "element",
            "app",
            "kebab-case"
        ]
    }
}

Using Redis in Laravel:

laravel 如何使用sorted sets?跟前一篇文章差不多。連到某一篇article頁面後,把key (trending_articles) 當中的成員,也就是這篇文章的id,分數加1。

Route::get('articles/{article}', function (App\Article $article) {
 
Redis::zincrby('trending_articles', 1, $article->id);
 
return $article;
 
});

articles/trending 頁面顯示熱門前3名的文章。
$trending = Redis::zrevrange('trending_articles', 0, 2);
 
return $trending;

sorted sets 的key或成員並不需要先用zadd加入,使用zincrby也能直接加產生資料。

成員以id來記錄,有時不是那麼容易看出來是什麼東東?也可以把整個model作為成員。laravel會把model轉成json格式再儲存。

Redis::zincrby('trending_articles', 1, $article);
 
// 以下語法等同上面
Redis::zincrby('trending_articles', 1, (string)$article);
Redis::zincrby('trending_articles', 1, $article->toJson());

但重點轉成json格式後,在怎麼再轉成看得懂的code?
$trending = App\Article::hydrate(
     array_map('json_decode', $trending)
);
這樣就會解出排名好的collection。

如果要移除成員,可以使用Redis::zremrangebyrank(‘trending_articles’, 0, -4),移除第四名以後的成員。建議寫成cron job定期刪除。

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.