12/08/2018, 15:55

Counter Cache trong Laravel

1. Vấn đề Input : Cho 2 bảng: Products (id, name, comment_count,..) và Comments (id, product_id, content,..) có quan hệ 1-N. Output : Tự động tăng hoặc giảm Products.comment_count khi tạo hoặc xóa comment. 2. Thực hiện Cách 1 : Cách đơn giản nhất là khi nào khi nào comment mới ...

1. Vấn đề

Input:

  • Cho 2 bảng: Products (id, name, comment_count,..) và Comments (id, product_id, content,..) có quan hệ 1-N.

Output:

  • Tự động tăng hoặc giảm Products.comment_count khi tạo hoặc xóa comment.

2. Thực hiện

  • Cách 1:
    • Cách đơn giản nhất là khi nào khi nào comment mới được tạo ta sẽ thực hiện tăng comment_count lên 1 đơn vị, khi nào comment bị xóa ta sẽ giảm comment_count đi 1 đơn vị:
    • Ở đây mình dùng trong Laravel, nên mình sẽ dùng migrate để tạo bảng
      Schema::create('products', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->integer('comments_count')->default(0); // This is the counter that you have to add
        $table->timestamps();
      });
      
      Schema::create('comments', function (Blueprint $table) {
       $table->increments('id');
       $table->integer('product_id');
       $table->string('content');
       $table->timestamps();
      });
      
      • Chạy lệnh sau để tạo bảng: php artisan migrate
    • Tiếp theo ta cần tạo giữa bảng comments và products trong model Comment:
          public function product()
          {
              return $this->belongsTo('AppProduct');
          }
      
    • Đến đây thì xong rồi, ta chỉ cần thêm đoạn tăng giảm comment_count trong controller CommentController hoặc CommentService khi xóa hoặc tạo comment mới:
      public function store(Request $request)
      {
          $data = $request->only(`content`, `product_id`);
          $comment = Comment::create($data);
          if ($comment) {
          		$comment->product->increament("comment_count");
          }
          ...
      }
      
      • Và xóa comment:
      public function delete($id)
      {
         $comment = Comment::find($id);
         if ($comment) {
         		$comment->product->decreament("comment_count");
         }
         ...
      }
      
    • Hoặc nếu bạn không muốn viết xử lý tăng comment_count trong controller hay service, bạn có thể bắt sự kiện created, deleted để tự động tăng comment_count:
      protected static function boot()
      {
          static::created(function ($comment) {
              $comment->product->increment('comment_count');
          });
      
          static::deleted(function ($comment) {
              $comment->product->decrement('comment_count');
          });
      }
      
      • Hàm này được viết trong model Comment
    • Cách trên có vẻ khá dễ và được ae sử dụng khá nhiều đúng không, Ở mình muốn giới thiệu đến 1 cách khác còn dễ hơn nữa:
  • Cách 2:
    • Sử dụng package Counter Cache for Laravel:

    • Thêm vào dòng sau vào composer.js:

      "kanazaca/counter-cache": "1.0.*"
      
    • Sau đó chạy lệnh composer install để cài đặt package

    • Thêm providers vào file config/app.php:

      'providers' => array(
           // ...
           kanazacaCounterCacheCounterCacheServiceProvider::class,
       )
      
    • Đến đây, thay vì làm như cách 1, ta chỉ cần include package vào model Comment và khai báo đúng chuẩn là xong, việc tăng giảm comment_count để package lo:

      namespace App;
      
       use IlluminateDatabaseEloquentModel;
       use kanazacaCounterCacheCounterCache;
      
       class Comments extends Model
       {
           use CounterCache;
      
           // you can have more than one counter
           public $counterCacheOptions = [
               'product' => [
                   'field' => 'comment_count',
                   'foreignKey' => 'product_id',
               ]
           ];
      
           public function product()
           {
               return $this->belongsTo('AppProduct');
           }
       }
      
    • Nếu bạn muốn filter trước khi tăng count bạn có thể làm như sau:

      public $counterCacheOptions = [
           'product' => [
               'field' => 'comment_count',
               'foreignKey' => 'product_id',
               'filter' => 'CommentValidatedFilter'
           ]
       ]; // you can have more than one counter 
      
       // this code will be executed before the counting (save and update method)
       public function CommentValidatedFilter() 
       {
           if ($this->validated)
           {
               return true;
           }
      
           return false;
       }
      

0