• Czas czytania ~4 min
  • 15.11.2023

Prawdopodobnie zbuforowałeś już niektóre dane modelu w kontrolerze, ale pokażę Ci technikę buforowania modeli Laravel, która jest nieco bardziej szczegółowa przy użyciu modeli Active Record. Jest to technika, o której po raz pierwszy dowiedziałem się na RailsCasts.

Korzystając z unikatowego klucza pamięci podręcznej w modelu, można buforować właściwości i skojarzenia w modelach, które są automatycznie aktualizowane (a pamięć podręczna unieważniana) po zaktualizowaniu modelu (lub skojarzonego modelu). Dodatkową korzyścią jest to, że dostęp do danych w pamięci podręcznej jest bardziej przenośny niż buforowanie danych w kontrolerze, ponieważ znajduje się on w modelu, a nie w ramach jednej metody kontrolera.

Oto istota techniki:

Załóżmy, że masz model, Article który ma wiele Comment modeli. Biorąc pod uwagę następujący szablon ostrza Laravel, możesz pobrać liczbę komentarzy w ten sposób na swojej /article/:id trasie:

<h3>{{ str_plural('Comment', $article->comments->count()) }}</h3>

Możesz buforować liczbę komentarzy w kontrolerze, ale kontroler może być dość brzydki, gdy masz wiele jednorazowych zapytań i danych, które musisz buforować. Korzystając z kontrolera, dostęp do danych w pamięci podręcznej również nie jest zbyt przenośny.

Możemy zbudować szablon, który trafi do bazy danych tylko wtedy, gdy artykuł zostanie zaktualizowany, a każdy kod, który ma dostęp do modelu, może pobrać wartość z pamięci podręcznej:

<h3>{{ str_plural('Comment', $article->cached_comments_count) }}</h3>

Korzystając z metody dostępu do modelu, zbuforujemy liczbę komentarzy na podstawie czasu ostatniej aktualizacji artykułu.

Jak więc zaktualizować kolumnę updated_at artykułu, gdy zostanie dodany lub usunięty nowy komentarz?

Wprowadź metodę dotykową.

Dotykanie modeli

Korzystając z metody modelutouch(), możemy zaktualizować kolumnę updated_at artykułu:

$ php artisan tinker

>>> $article = \App\Article::first();
=> App\Article {#746
     id: 1,
     title: "Hello World",
     body: "The Body",
     created_at: "2018-01-11 05:16:51",
     updated_at: "2018-01-11 05:51:07",
   }
>>> $article->updated_at->timestamp
=> 1515649867
>>> $article->touch();
=> true
>>> $article->updated_at->timestamp
=> 1515650910

Możemy użyć zaktualizowanego znacznika czasu, aby unieważnić pamięć podręczną, ale jak możemy dotknąć pola artykułuupdated_at, gdy dodajemy lub usuwamy komentarz?

Tak się składa, że modele Eloquent mają właściwość o nazwie $touches. Oto, jak może wyglądać nasz model komentarzy:

namespace App;

use App\Article;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $guarded = [];

    protected $touches = ['article'];

    public function article()
    {
        return $this->belongsTo(Article::class);
    }
}

Właściwość $touches jest tablicą zawierającą skojarzenie, które zostanie "dotknięte" podczas tworzenia, zapisywania lub usuwania komentarza.

Atrybut

buforowany Wróćmy do $article->cached_comments_count akcesora. Implementacja może wyglądać następująco App\Article w modelu:

public function getCachedCommentsCountAttribute()
{
    return Cache::remember($this->cacheKey() . ':comments_count', 15, function () {
        return $this->comments->count();
    });
}

Buforujemy model przez piętnaście minut przy użyciu unikalnej cacheKey() metody i po prostu zwracamy liczbę komentarzy wewnątrz zamknięcia.

Pamiętaj, że możemy również użyć tej Cache::rememberForever() metody i polegać na wyrzucaniu śmieci naszego mechanizmu buforowania, aby usunąć stare klucze. Ustawiłem licznik czasu tak, aby pamięć podręczna była trafiana przez większość czasu, z nową pamięcią podręczną co piętnaście minut.

Metoda cacheKey() musi sprawić, że model będzie unikatowy i unieważnić pamięć podręczną po zaktualizowaniu modelu. Oto moja cacheKey implementacja:Przykładowe dane wyjściowe metody modelu mogą zwrócić następującą reprezentację ciągu:

public function cacheKey()
{
    return sprintf(
        "%s/%s-%s",
        $this->getTable(),
        $this->getKey(),
        $this->updated_at->timestamp
    );
}

articles/1-1515650910

Klucz to nazwa tabeli, identyfikator modelu cacheKey() i bieżący updated_at znacznik czasu. Gdy dotkniemy modelu, znacznik czasu zostanie zaktualizowany, a nasza pamięć podręczna modelu zostanie odpowiednio unieważniona.

Oto model, Article jeśli jest pełny:I powiązany Comment model:

namespace App;

use App\Comment;
use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    public function cacheKey()
    {
        return sprintf(
            "%s/%s-%s",
            $this->getTable(),
            $this->getKey(),
            $this->updated_at->timestamp
        );
    }
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
    public function getCachedCommentsCountAttribute()
    {
        return Cache::remember($this->cacheKey() . ':comments_count', 15, function () {
            return $this->comments->count();
        });
    }
}

namespace App;

use App\Article;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $guarded = [];

    protected $touches = ['article'];

    public function article()
    {
        return $this->belongsTo(Article::class);
    }
}

Co dalej?

Pokazałem ci, jak buforować prostą liczbę komentarzy, ale co z buforowaniem wszystkich komentarzy?

public function getCachedCommentsAttribute()
{
    return Cache::remember($this->cacheKey() . ':comments', 15, function () {
        return $this->comments;
    });
}

Możesz również zdecydować się na konwersję komentarzy na tablicę zamiast serializacji modeli, aby umożliwić tylko prosty dostęp do tablicy do danych w interfejsie użytkownika:Na koniec zdefiniowałem metodę na Article modelu, ale chciałbyś zdefiniować tę metodę za pomocą cechy o nazwie coś takiegoProvidesModelCacheKey,

public function getCachedCommentsAttribute()
{
    return Cache::remember($this->cacheKey() . ':comments', 15, function () {
        return $this->comments->toArray();
    });
}

której możesz użyć w wielu modelach lub zdefiniować cacheKey() metodę w modelu podstawowym, który rozszerzają wszystkie nasze modele. Możesz nawet użyć kontraktu (interfejsu) dla modeli, które implementują metodęcacheKey().

Mam nadzieję, że ta prosta technika okazała się przydatna!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297