Документация Laravel 10.x
Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.
Некоторые из задач по извлечению или обработке данных, выполняемых вашим приложением, могут быть ресурсоемкими в плане использования процессора или занимать несколько секунд для завершения. В таких случаях обычно кешируют извлеченные данные на какое-то время, чтобы они могли быть быстро получены при последующих запросах к тем же данным. Кешированные данные обычно хранятся в очень быстром хранилище данных, таком как Memcached или Redis.
К счастью, Laravel предоставляет выразительный, унифицированный API для различных кеш-бэков, позволяя вам воспользоваться их мгновенным извлечением данных и ускорить ваше веб-приложение.
Конфигурационный файл кеша вашего приложения находится в config/cache.php
. В этом файле вы можете указать, какой драйвер кеша вы хотели бы использовать по умолчанию в вашем приложении. Laravel поддерживает популярные кеш-бэкинды, такие как Memcached, Redis, DynamoDB, а также реляционные базы данных. Кроме того, доступны драйверы кеша на основе файлов, а драйверы array
и "null" предоставляют удобные бекенды кеша для ваших автоматизированных тестов.
Файл конфигурации кеша также содержит различные другие параметры, которые документированы внутри файла, так что убедитесь, что вы прочитали эти параметры. По умолчанию Laravel настроен на использование драйвера кеша file
, который хранит сериализованные объекты кеша на файловой системе сервера. Для крупных приложений рекомендуется использовать более надежный драйвер, такой как Memcached или Redis. Вы даже можете настроить несколько конфигураций кеша для одного и того же драйвера.
При использовании драйвера кеша database
вам потребуется настроить таблицу для хранения элементов кеша. Приведен пример объявления схемы для этой таблицы ниже:
Schema::create('cache', function (Blueprint $table) { $table->string('key')->unique(); $table->text('value'); $table->integer('expiration');});
Примечание Вы также можете использовать команду Artisan
php artisan cache:table
для создания миграции с правильной схемой.
Использование драйвера Memcached требует установки пакета Memcached PECL. Вы можете перечислить все серверы Memcached в файле конфигурации config/cache.php
. Этот файл уже содержит запись memcached.servers
, чтобы вам было с чем начать:
'memcached' => [ 'servers' => [ [ 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 'port' => env('MEMCACHED_PORT', 11211), 'weight' => 100, ], ],],
При необходимости вы можете установить параметр host
на путь к файловому сокету UNIX. Если вы сделаете это, параметр port
должен быть установлен в 0
:
'memcached' => [ [ 'host' => '/var/run/memcached/memcached.sock', 'port' => 0, 'weight' => 100 ],],
Перед использованием кеша Redis с Laravel вам необходимо установить расширение PhpRedis для PHP через PECL или установить пакет predis/predis
(~1.0) через Composer. Laravel Sail уже включает это расширение. Кроме того, официальные платформы развертывания Laravel, такие как Laravel Forge и Laravel Vapor, по умолчанию устанавливают расширение PhpRedis.
Дополнительную информацию по настройке Redis можно найти на его странице документации Laravel.
Перед использованием драйвера кеша DynamoDB вы должны создать таблицу DynamoDB для хранения всех кешированных данных. Обычно эта таблица должна иметь имя cache
. Однако вы должны назвать таблицу в соответствии со значением конфигурационного параметра stores.dynamodb.table
в файле конфигурации cache
вашего приложения.
Эта таблица также должна иметь строковый разделительный ключ с именем, соответствующим значению конфигурационного параметра stores.dynamodb.attributes.key
в файле конфигурации cache
вашего приложения. По умолчанию разделительный ключ должен иметь имя key
.
Для получения экземпляра кеша вы можете использовать фасад Cache
, который мы будем использовать в этой документации. Фасад Cache
предоставляет удобный и краткий доступ к основным реализациям контрактов кеша Laravel:
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Cache; class UserController extends Controller{ /** * Показать список всех пользователей приложения. */ public function index(): array { $value = Cache::get('key'); return [ // ... ]; }}
Используя фасад Cache
, вы можете получить доступ к различным хранилищам кеша с помощью метода store
. Ключ, передаваемый методу store
, должен соответствовать одному из хранилищ, перечисленных в массиве конфигурации stores
в файле конфигурации cache
:
$value = Cache::store('file')->get('foo'); Cache::store('redis')->put('bar', 'baz', 600); // 10 Минут
Метод get
фасада Cache
используется для извлечения элементов из кеша. Если элемент не существует в кеше, будет возвращено значение null
. Если вы хотите, вы можете передать второй аргумент методу get
, указывающий значение по умолчанию, которое вы хотели бы получить, если элемент не существует:
$value = Cache::get('key'); $value = Cache::get('key', 'default');
Вы даже можете передать замыкание в качестве значения по умолчанию. Результат выполнения замыкания будет возвращен, если указанный элемент не существует в кеше. Передача замыкания позволяет отложить получение значений по умолчанию из базы данных или другого внешнего сервиса:
$value = Cache::get('key', function () { return DB::table(/* ... */)->get();});
Метод has
можно использовать для определения того, существует ли элемент в кеше. Этот метод также вернет false
, если элемент существует, но его значение равно null
:
if (Cache::has('key')) { // ...}
Методы increment
и decrement
могут использоваться для корректировки значения целочисленных элементов в кеше. Оба этих метода принимают необязательный второй аргумент, указывающий количество, на которое следует увеличить или уменьшить значение элемента:
// Инициализировать значение, если оно не существует...Cache::add('key', 0, now()->addHours(4)); // Увеличить или уменьшить значение...Cache::increment('key');Cache::increment('key', $amount);Cache::decrement('key');Cache::decrement('key', $amount);
Иногда вам может потребоваться извлечь элемент из кеша, но также сохранить значение по умолчанию, если запрошенный элемент не существует. Например, вы можете извлечь всех пользователей из кеша или, если их нет, извлечь их из базы данных и добавить их в кеш. Вы можете сделать это с помощью метода Cache::remember
:
$value = Cache::remember('users', $seconds, function () { return DB::table('users')->get();});
Если элемент не существует в кеше, выполнится замыкание, переданное методу remember
, и его результат будет помещен в кеш.
Вы можете использовать метод rememberForever
, чтобы извлечь элемент из кеша или сохранить его навсегда, если он не существует:
$value = Cache::rememberForever('users', function () { return DB::table('users')->get();});
Если вам нужно извлечь элемент из кеша, а затем удалить его, вы можете использовать метод pull
. Как и метод get
, он вернет null
, если элемент не существует в кеше:
$value = Cache::pull('key');
Метод put
фасада Cache
используется для сохранения элементов в кеше:
Cache::put('key', 'value', $seconds = 10);
Если время хранения не передается методу put
, элемент будет сохранен на неопределенный срок:
Cache::put('key', 'value');
Вместо передачи количества секунд в виде целого числа вы также можете передать экземпляр DateTime
, представляющий желаемое время истечения срока действия кешируемого элемента:
Cache::put('key', 'value', now()->addMinutes(10));
Метод add
добавит элемент в кеш только в том случае, если он еще не существует в хранилище кеша. Метод вернет true
, если элемент действительно добавлен в кеш. В противном случае метод вернет false
. Метод add
является атомарной операцией:
Cache::add('key', 'value', $seconds);
Метод forever
может использоваться для сохранения элемента в кеше навсегда. Поскольку эти элементы не истекают, их следует удалить из кеша вручную с помощью метода forget
:
Cache::forever('key', 'value');
Примечание Если вы используете драйвер Memcached, элементы, сохраненные "навсегда", могут быть удалены, когда кеш достигнет своего лимита по размеру.
Вы можете удалить элементы из кеша с помощью метода forget
:
Cache::forget('key');
Вы также можете удалить элементы, указав ноль или отрицательное количество секунд их существования:
Cache::put('key', 'value', 0); Cache::put('key', 'value', -5);
Вы можете очистить весь кеш с помощью метода flush
:
Cache::flush();
Внимание Очистка кеша не учитывает ваш настроенный кеш "prefix" и удалит все записи из кеша. Внимательно обдумайте это при очистке кеша, который используется другими приложениями.
Помимо использования фасада Cache
, вы также можете использовать глобальную функцию cache
для извлечения и сохранения данных через кеш. Когда функция cache
вызывается с единственным строковым аргументом, она вернет значение для указанного ключа:
$value = cache('key');
Если вы предоставляете функции массив пар ключ / значение и время жизни, она сохранит значения в кеше на указанное время:
cache(['key' => 'value'], $seconds); cache(['key' => 'value'], now()->addMinutes(10));
Когда функция cache
вызывается без аргументов, она возвращает экземпляр реализации Illuminate\Contracts\Cache\Factory
, позволяя вам вызывать другие методы кеширования:
cache()->remember('users', $seconds, function () { return DB::table('users')->get();});
Примечание При тестировании вызова глобальной функции
cache
вы можете использовать методCache::shouldReceive
, так же, как если бы вы тестировали фасад.
Внимание Для использования этой функции ваше приложение должно использовать драйвер кеша
memcached
,redis
,dynamodb
,database
,file
илиarray
в качестве драйвера кеша по умолчанию. Кроме того, все серверы должны взаимодействовать с одним и тем же центральным сервером кеша.
При использовании драйвера кеша database
вам нужно настроить таблицу для хранения блокировок кеша вашего приложения. Приведен пример объявления схемы для этой таблицы ниже:
Schema::create('cache_locks', function (Blueprint $table) { $table->string('key')->primary(); $table->string('owner'); $table->integer('expiration');});
Примечание Если вы использовали команду Artisan
cache:table
для создания таблицы кеша драйвера базы данных, миграция, созданная этой командой, уже включает определение таблицыcache_locks
.
Атомарные блокировки позволяют манипулировать распределенными блокировками, не беспокоясь о гонках. Например, Laravel Forge использует атомарные блокировки, чтобы гарантировать, что на сервере одновременно выполняется только одна удаленная задача. Вы можете создавать и управлять блокировками с использованием метода Cache::lock
:
use Illuminate\Support\Facades\Cache; $lock = Cache::lock('foo', 10); if ($lock->get()) { // Блокировка получена на 10 секунд... $lock->release();}
Метод get
также принимает замыкание. После выполнения замыкания Laravel автоматически освободит блокировку:
Cache::lock('foo', 10)->get(function () { // Блокировка получена на 10 секунд и автоматически освобождена...});
Если блокировка в данный момент недоступна, вы можете указать Laravel ждать определенное количество секунд. Если блокировку нельзя получить в течение указанного времени, будет сгенерировано исключение Illuminate\Contracts\Cache\LockTimeoutException
:
use Illuminate\Contracts\Cache\LockTimeoutException; $lock = Cache::lock('foo', 10); try { $lock->block(5); // Блокировка получена после ожидания максимум 5 секунд...} catch (LockTimeoutException $e) { // Невозможно получить блокировку...} finally { $lock?->release();}
В приведенном выше примере можно упростить использование замыкания с методом block
. Если замыкание передается этому методу, Laravel попытается получить блокировку в течение указанного количества секунд и автоматически освободит блокировку после выполнения замыкания:
Cache::lock('foo', 10)->block(5, function () { // Блокировка получена после ожидания максимум 5 секунд...});
Иногда вам может потребоваться получить блокировку в одном процессе и освободить ее в другом процессе. Например, вы можете получить блокировку во время веб-запроса и хотите освободить блокировку в конце очередной задачи, которая вызывается этим запросом. В этом случае вы должны передать "токен владельца" блокировки в очередную задачу, чтобы задача могла повторно создать блокировку, используя предоставленный токен.
В приведенном ниже примере мы отправим очередную задачу, если блокировка успешно получена. Кроме того, мы передадим токен владельца блокировки в очередную задачу с помощью метода owner
блокировки:
$podcast = Podcast::find($id); $lock = Cache::lock('processing', 120); if ($lock->get()) { ProcessPodcast::dispatch($podcast, $lock->owner());}
В рамках нашей задачи ProcessPodcast
приложения мы можем восстановить и освободить блокировку с использованием токена владельца:
Cache::restoreLock('processing', $this->owner)->release();
Если вы хотите освободить блокировку без учета ее текущего владельца, вы можете использовать метод forceRelease
:
Cache::lock('processing')->forceRelease();
Чтобы создать наш собственный драйвер кеша, сначала нам нужно реализовать контракт Illuminate\Contracts\Cache\Store
. Таким образом, реализация кеша MongoDB может выглядеть примерно так:
<?php namespace App\Extensions; use Illuminate\Contracts\Cache\Store; class MongoStore implements Store{ public function get($key) {} public function many(array $keys) {} public function put($key, $value, $seconds) {} public function putMany(array $values, $seconds) {} public function increment($key, $value = 1) {} public function decrement($key, $value = 1) {} public function forever($key, $value) {} public function forget($key) {} public function flush() {} public function getPrefix() {}}
Нам просто нужно реализовать каждый из этих методов с использованием соединения MongoDB. Для примера того, как реализовать каждый из этих методов, посмотрите Illuminate\Cache\MemcachedStore
в исходном коде фреймворка Laravel. После завершения нашей реализации мы можем завершить регистрацию нашего собственного драйвера, вызвав метод extend
фасада Cache
:
Cache::extend('mongo', function (Application $app) { return Cache::repository(new MongoStore);});
Примечание Если вы задаетесь вопросом, где разместить свой код для пользовательского драйвера кеша, вы можете создать пространство имен
Extensions
внутри вашего каталогаapp
. Тем не менее, имейте в виду, что у Laravel нет жесткой структуры приложения, и вы свободны организовывать свое приложение в соответствии с вашими предпочтениями.
Для регистрации собственного драйвера кеша с Laravel мы будем использовать метод extend
фасада Cache
. Поскольку другие службы могут попытаться читать кешированные значения в своем методе boot
, мы зарегистрируем наш собственный драйвер в пределах обратного вызова booting
. Используя обратный вызов booting
, мы можем убедиться, что собственный драйвер зарегистрирован непосредственно перед вызовом метода boot
служб-поставщиков приложения, но после вызова метода register
всех служб-поставщиков. Мы зарегистрируем наш обратный вызов booting
в методе register
класса App\Providers\AppServiceProvider
нашего приложения:
<?php namespace App\Providers; use App\Extensions\MongoStore;use Illuminate\Contracts\Foundation\Application;use Illuminate\Support\Facades\Cache;use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider{ /** * Зарегистрировать любые службы приложения. */ public function register(): void { $this->app->booting(function () { Cache::extend('mongo', function (Application $app) { return Cache::repository(new MongoStore); }); }); } /** * Инициализировать любые службы приложения. */ public function boot(): void { // ... }}
Первым аргументом, переданным методу extend
, является имя драйвера. Это соответствует вашему параметру driver
в файле конфигурации config/cache.php
. Вторым аргументом является замыкание, которое должно возвращать экземпляр Illuminate\Cache\Repository
. Замыкание получит экземпляр $app
, который является экземпляром контейнера зависимостей.
После регистрации вашего расширения обновите параметр driver
файла конфигурации config/cache.php
на имя вашего расширения.
Чтобы выполнить код при каждой операции кеша, вы можете прослушивать события, вызываемые кешем. Обычно вы должны размещать эти слушатели событий в классе App\Providers\EventServiceProvider
вашего приложения:
use App\Listeners\LogCacheHit;use App\Listeners\LogCacheMissed;use App\Listeners\LogKeyForgotten;use App\Listeners\LogKeyWritten;use Illuminate\Cache\Events\CacheHit;use Illuminate\Cache\Events\CacheMissed;use Illuminate\Cache\Events\KeyForgotten;use Illuminate\Cache\Events\KeyWritten; /** * Сопоставления слушателей событий для приложения. * * @var array */protected $listen = [ CacheHit::class => [ LogCacheHit::class, ], CacheMissed::class => [ LogCacheMissed::class, ], KeyForgotten::class => [ LogKeyForgotten::class, ], KeyWritten::class => [ LogKeyWritten::class, ],];