diff --git a/README.md b/README.md index 5da1121..f9fc533 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ - [x] [Установка](./docs/installation.md) - [x] [Конфигурирование](./docs/configuration.md) - [x] [Структура каталогов](./docs/structure.md) + - [ ] [Внешний интерфейс приложения](./docs/frontend.md) - [x] [Стартовые комплекты](./docs/starter-kits.md) - [x] [Развертывание](./docs/deployment.md) - #### Архитектурные концепции @@ -35,6 +36,7 @@ - [x] [HTTP-ответы](./docs/responses.md) - [x] [HTML-шаблоны](./docs/views.md) - [x] [Шаблонизатор Blade](./docs/blade.md) + - [ ] [Объединение веб-активов](./docs/vite.md) - [x] [Генерация URL-адресов](./docs/urls.md) - [x] [Сессия HTTP](./docs/session.md) - [x] [Валидация](./docs/validation.md) @@ -45,7 +47,6 @@ - [x] [Трансляция событий](./docs/broadcasting.md) - [x] [Кеш приложения](./docs/cache.md) - [x] [Коллекции](./docs/collections.md) - - [x] [Компиляция веб-активов с помощью Mix](./docs/mix.md) - [x] [Контракты](./docs/contracts.md) - [x] [События](./docs/events.md) - [x] [Файловое хранилище](./docs/filesystem.md) @@ -79,6 +80,7 @@ - [x] [Мутаторы и типизация](./docs/eloquent-mutators.md) - [x] [Ресурсы API](./docs/eloquent-resources.md) - [x] [Сериализация](./docs/eloquent-serialization.md) + - [ ] [Фабрики](./docs/eloquent-factories.md) - #### Тестирование - [x] [Начало работы](./docs/testing.md) - [x] [Тесты HTTP](./docs/http-tests.md) @@ -96,8 +98,10 @@ - [x] [*Homestead*](./docs/homestead.md) – официальный образ Vagrant для приложений Laravel. - [x] [*Horizon*](./docs/horizon.md) – панель управления и конфигурация очередей, использующих Redis. - [ ] [*Jetstream*](https://jetstream.laravel.com) – красиво оформленный каркас приложений. Включает в себя Fortify и Sanctum. + - [x] [*Mix*](./docs/mix.md) – гибкий API для определения шагов сборки Webpack; упрощает компиляцию и минимизацию файлов CSS и JavaScript. - [ ] [*Octane*](./docs/octane.md) – повышает производительность вашего приложения с использованием мощных серверов [Swoole](https://swoole.co.uk) и [RoadRunner](https://roadrunner.dev) - [ ] [*Passport*](./docs/passport.md) – реализация сервера OAuth2 для вашего приложения Laravel на основе [League OAuth2](https://github.com/thephpleague/oauth2-server). + - [ ] [*Pint*](./docs/pint.md) – is an opinionated PHP code style fixer for minimalists. - [x] [*Sail*](./docs/sail.md) – CLI для взаимодействия со средой разработки Docker. - [x] [*Sanctum*](./docs/sanctum.md) – легковесная система аутентификации для SPA (одностраничных приложений), мобильных приложений и простых API на основе токенов. Управление токенами API, аутентификация сессии. Не содержит никаких шаблонов. Используется в Laravel Jetstream. - [x] [*Scout*](./docs/scout.md) – «простое» решение на основе драйверов для добавления полнотекстового поиска моделям Eloquent. diff --git a/docs/artisan.md b/docs/artisan.md index a5e3949..f09b8ea 100644 --- a/docs/artisan.md +++ b/docs/artisan.md @@ -6,6 +6,7 @@ - [Генерация команд](#generating-commands) - [Структура команды](#command-structure) - [Анонимные команды](#closure-commands) + - [Изолируемые команды](#isolatable-commands) - [Определение вводимых данных](#defining-input-expectations) - [Аргументы](#arguments) - [Параметры](#options) @@ -60,7 +61,8 @@ Laravel Tinker – это мощный REPL для фреймворка Laravel, composer require laravel/tinker ``` -> {tip} Ищете графический интерфейс для взаимодействия с приложением Laravel? Зацените [Tinkerwell](https://tinkerwell.app)! +> **Примечание**\ +> Ищете графический интерфейс для взаимодействия с приложением Laravel? Зацените [Tinkerwell](https://tinkerwell.app)! #### Использование @@ -77,7 +79,8 @@ php artisan tinker php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider" ``` -> {note} Глобальный помощник `dispatch` и метод `dispatch` класса `Dispatchable` зависят от "garbage collection" для помещения задания в очередь. Следовательно, при использовании Tinker вы должны использовать `Bus::dispatch` или `Queue::push` для отправки заданий. +> **Предупреждение**\ +> Глобальный помощник `dispatch` и метод `dispatch` класса `Dispatchable` зависят от "garbage collection" для помещения задания в очередь. Следовательно, при использовании Tinker вы должны использовать `Bus::dispatch` или `Queue::push` для отправки заданий. #### Список разрешенных команд @@ -154,7 +157,8 @@ php artisan make:command SendEmails } } -> {tip} Хорошей практикой повторного использования кода считается создание «простых» консольных команд с делегированием своих задач службам приложения. В приведенном примере мы внедряем класс службы для выполнения «затратной» отправки электронных писем. +> **Примечание**\ +> Хорошей практикой повторного использования кода считается создание «простых» консольных команд с делегированием своих задач службам приложения. В приведенном примере мы внедряем класс службы для выполнения «затратной» отправки электронных писем. ### Анонимные команды @@ -200,6 +204,55 @@ php artisan make:command SendEmails // ... })->purpose('Send a marketing email to a user'); + +### Изолируемые команды + +> **Предупреждение**\ +> Чтобы использовать этот функционал, ваше приложение должно использовать в качестве драйвера кеша по умолчанию `memcached`, `redis`, `dynamodb`, `database`, `file`, `array`. Кроме того, все серверы должны обмениваться данными с одним и тем же сервером центрального кэша. + +Иногда необходимо гарантировать, что только один экземпляр команды может выполняться одновременно. Для этого вы можете реализовать интерфейс `Illuminate\Contracts\Console\Isolatable` в своем классе команд: + + +#### Время действия блокировки + +По умолчанию время действия изоляционных блокировок истекает после завершения команды. Или, если команда прервана и не может быть завершена, блокировка истечет через один час. Однако вы можете настроить время истечения блокировки, определив метод `isolationLockExpiresAt` в вашей команде: + +```php +/** + * Определить, когда истечет время действия блокировки изоляции команды. + * + * @return \DateTimeInterface|\DateInterval + */ +public function isolationLockExpiresAt() +{ + return now()->addMinutes(5); +} +``` + ## Определение вводимых данных @@ -495,7 +548,8 @@ php artisan mail:send --id=1 --id=2 $bar->finish(); -> {tip} Для получения дополнительной информации ознакомьтесь с [разделом документации компонента Symfony Progress Bar](https://symfony.com/doc/current/components/console/helpers/progressbar.html). +> **Примечание**\ +> Для получения дополнительной информации ознакомьтесь с [разделом документации компонента Symfony Progress Bar](https://symfony.com/doc/current/components/console/helpers/progressbar.html). ## Регистрация команд @@ -611,47 +665,29 @@ php artisan mail:send --id=1 --id=2 ## Обработка сигналов -Компонент Symfony Console, на котором работает консоль Artisan, позволяет вам указать, какие [сигналы процесса](https://ru.wikipedia.org/wiki/Сигнал_(Unix)) (если есть) может обрабатывать ваша команда. Например, вы можете указать, что ваша команда может обрабатывать сигналы `SIGINT` и `SIGTERM`. - -Для начала вы должны реализовать интерфейс `Symfony\Component\Console\Command\SignalableCommandInterface` в классе своей команды Artisan. Этот интерфейс требует от вас определения двух методов: `getSubscribedSignals` и `handleSignal`: - -```php -stopServer(); + $this->trap(SIGTERM, fn () => $this->shouldKeepRunning = false); - return; + while ($this->shouldKeepRunning) { + // ... } } -} -``` -Метод `getSubscribedSignals` должен возвращать массив сигналов, которые может обработать ваша команда, в то время как метод `handleSignal` принимает сигнал и может реагировать на него в соответствии с определенной вами логикой. +Чтобы прослушивать несколько сигналов одновременно, вы можете передать массив сигналов методу `trap`: + + $this->trap([SIGTERM, SIGQUIT], function ($signal) { + $this->shouldKeepRunning = false; + + dump($signal); // SIGTERM / SIGQUIT + }); ## Настройка заготовок diff --git a/docs/authentication.md b/docs/authentication.md index 5557e34..901b5fa 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -39,7 +39,8 @@ Файл конфигурации аутентификации вашего приложения находится в `config/auth.php`. Этот файл содержит несколько хорошо задокументированных вариантов для настройки поведения служб аутентификации Laravel. -> {tip} Охранников и провайдеров не следует путать с «ролями» и «разрешениями». Чтобы узнать больше об авторизации действий пользователя с помощью разрешений, обратитесь к документации по [авторизации](authorization.md). +> **Примечание**\ +> Охранников и провайдеров не следует путать с «ролями» и «разрешениями». Чтобы узнать больше об авторизации действий пользователя с помощью разрешений, обратитесь к документации по [авторизации](authorization.md). ### Стартовые комплекты @@ -79,7 +80,7 @@ Laravel содержит встроенные службы аутентифик [**Laravel Fortify**](fortify.md) – это лишь серверная часть аутентификации для Laravel, которая реализует многие возможности, описанные в этой документации, включая аутентификацию на основе файлов cookie, а также другие возможности, такие как двухфакторная аутентификация и проверка электронной почты. Fortify обеспечивает серверную реализацию аутентификации для Laravel Jetstream, но может использоваться и независимо в сочетании с [Laravel Sanctum](sanctum.md) для обеспечения одностраничных приложений (SPA) возможностью аутентификацией с Laravel. -[**Laravel Jetstream**](https://jetstream.laravel.com) – это надежный стартовый комплект, который использует и предлагает службы аутентификации Laravel Fortify, но с красивым современным пользовательским интерфейсом на основе [Tailwind CSS](https://tailwindcss.com), [Livewire](https://laravel-livewire.com) и / или [Inertia.js](https://inertiajs.com). Laravel Jetstream дополнительно включает поддержку двухфакторной аутентификации, поддержку команды, управление сеансами браузера, управление профилями и встроенную интеграцию с [Laravel Sanctum](sanctum.md) для аутентификации токена API. +[**Laravel Jetstream**](https://jetstream.laravel.com) – это надежный стартовый комплект, который использует и предлагает службы аутентификации Laravel Fortify, но с красивым современным пользовательским интерфейсом на основе [Tailwind CSS](https://tailwindcss.com), [Livewire](https://laravel-livewire.com) и / или [Inertia](https://inertiajs.com). Laravel Jetstream дополнительно включает поддержку двухфакторной аутентификации, поддержку команды, управление сеансами браузера, управление профилями и встроенную интеграцию с [Laravel Sanctum](sanctum.md) для аутентификации токена API. #### Службы API-аутентификации Laravel @@ -109,12 +110,13 @@ Laravel Sanctum – это пакет API, который мы выбрали д Passport можно выбрать, если вашему приложению необходим абсолютно весь функционал, предоставляемый спецификацией OAuth2. -И, если вы хотите быстро начать работу, то мы рады порекомендовать пакет [Laravel Jetstream](https://jetstream.laravel.com) как быстрый способ запустить новое приложение Laravel, который уже использует предпочтительный стек аутентификации: встроенные службы аутентификации Laravel и Laravel Sanctum. +И, если вы хотите быстро начать работу, то мы рады порекомендовать пакет [Laravel Breeze](starter-kits.md#laravel-breeze) как быстрый способ запустить новое приложение Laravel, который уже использует предпочтительный стек аутентификации: встроенные службы аутентификации Laravel и Laravel Sanctum. ## Быстрый запуск аутентификации -> {note} В этой части документации обсуждается аутентификация пользователей с помощью [стартовых комплектов Laravel](starter-kits.md), которые включают в себя каркас пользовательского интерфейса, который поможет вам быстро начать работу. Если вы хотите напрямую интегрироваться с системами аутентификации Laravel, то ознакомьтесь с документацией по [самостоятельной аутентификации пользователей](#authenticating-users). +> **Предупреждение**\ +> В этой части документации обсуждается аутентификация пользователей с помощью [стартовых комплектов Laravel](starter-kits.md), которые включают в себя каркас пользовательского интерфейса, который поможет вам быстро начать работу. Если вы хотите напрямую интегрироваться с системами аутентификации Laravel, то ознакомьтесь с документацией по [самостоятельной аутентификации пользователей](#authenticating-users). ### Установка стартовых комплектов @@ -123,7 +125,7 @@ Passport можно выбрать, если вашему приложению Laravel Breeze – это минимальная и простая реализация всех возможностей аутентификации Laravel, включая вход в систему, регистрацию, сброс пароля, подтверждение электронной почты и подтверждение пароля. Слой представления Laravel Breeze состоит из простых [шаблонов Blade](blade.md), стилизованных с помощью [Tailwind CSS](https://tailwindcss.com). Breeze также предлагает вариант создания каркасов на основе [Inertia][Inertia](https://inertiajs.com) с использованием Vue или React. -[Laravel Jetstream](https://jetstream.laravel.com) – это более надежный стартовый комплект для приложений, который включает поддержку построения вашего приложения с помощью [Livewire](https://laravel-livewire.com) или [Inertia.js и Vue](https://inertiajs.com). Кроме того, Jetstream предлагает дополнительную поддержку двухфакторной аутентификации, команд, управления профилями, управления сеансами браузера, поддержки API через [Laravel Sanctum](sanctum.md), удаления аккаунтов и т. д. +[Laravel Jetstream](https://jetstream.laravel.com) – это более надежный стартовый комплект для приложений, который включает поддержку построения вашего приложения с помощью [Livewire](https://laravel-livewire.com) или [Inertia и Vue](https://inertiajs.com). Кроме того, Jetstream предлагает дополнительную поддержку двухфакторной аутентификации, команд, управления профилями, управления сеансами браузера, поддержки API через [Laravel Sanctum](sanctum.md), удаления аккаунтов и т. д. ### Получение аутентифицированного пользователя @@ -171,7 +173,8 @@ Laravel Breeze – это минимальная и простая реализ // } -> {tip} Несмотря на то, что можно определить, аутентифицирован ли пользователь с помощью метода `check`, вы обычно будете использовать посредника для проверки статуса аутентификации пользователя перед предоставлением пользователю доступа к определенным маршрутам / контроллерам. Чтобы узнать больше об этом, ознакомьтесь с документацией по [защите маршрутов](#protecting-routes). +> **Примечание**\ +> Несмотря на то, что можно определить, аутентифицирован ли пользователь с помощью метода `check`, вы обычно будете использовать посредника для проверки статуса аутентификации пользователя перед предоставлением пользователю доступа к определенным маршрутам / контроллерам. Чтобы узнать больше об этом, ознакомьтесь с документацией по [защите маршрутов](#protecting-routes). ### Защита маршрутов @@ -212,7 +215,8 @@ Laravel Breeze – это минимальная и простая реализ Если вы используете [стартовые комплекты](starter-kits.md) Laravel Breeze или Laravel Jetstream, то к попыткам входа в систему будет автоматически применяться ограничение. По умолчанию, если пользователь не сможет предоставить правильные учетные данные после нескольких попыток, то он не сможет войти в систему в течение одной минуты. Частота попыток уникальна для имени пользователя / адреса электронной почты и в совокупности с IP-адресом. -> {tip} Если вы хотите ограничить частоту запросов к другим маршрутам своего приложении, то ознакомьтесь с [документацией по ограничению частоты запросов](routing.md#rate-limiting). +> **Примечание**\ +> Если вы хотите ограничить частоту запросов к другим маршрутам своего приложении, то ознакомьтесь с [документацией по ограничению частоты запросов](routing.md#rate-limiting). ## Самостоятельная реализация аутентификации пользователей @@ -269,10 +273,32 @@ Laravel Breeze – это минимальная и простая реализ При желании вы также можете добавить дополнительные условия запроса к запросу аутентификации в дополнение к электронной почте и паролю пользователя. Для этого мы можем просто добавить условия запроса в массив, переданному методу `attempt`. Например, мы можем проверить, что пользователь отмечен как «активный»: if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { - // Authentication was successful... + // Аутентификация прошла успешно ... } -> {note} В этих примерах `email` не является обязательным параметром, он просто используется в качестве примера. Вы должны использовать любое имя столбца, равнозначное «имени пользователя» в таблице базы данных. +Для сложных условий запроса вы можете задать замыкание в массиве учетных данных. Это замыкание будет вызываться вместе с экземпляром запроса, что позволит вам выстроить запрос в соответствии с потребностями вашего приложения: + + if (Auth::attempt([ + 'email' => $email, + 'password' => $password, + fn ($query) => $query->has('activeSubscription'), + ])) { + // Аутентификация прошла успешно ... + } + +> **Предупреждение**\ +> В этих примерах `email` не является обязательным параметром, он просто используется в качестве примера. Вы должны использовать любое имя столбца, равнозначное «имени пользователя» в таблице базы данных. + +Метод `attemptWhen`, который получает замыкание в качестве второго аргумента, может использоваться для выполнения более тщательной проверки потенциального пользователя перед его фактической аутентификацией. Замыкание получает потенциального пользователя и должно возвращать `true` или `false`, что указывает на то, может ли пользователь быть аутентифицирован: + + if (Auth::attemptWhen([ + 'email' => $email, + 'password' => $password, + ], function ($user) { + return $user->isNotBanned(); + })) { + // Аутентификация прошла успешно ... + } #### Доступ к конкретному экземпляру охранника аутентификации @@ -453,7 +479,8 @@ Laravel также предлагает механизм для «выхода» При создании приложения вы можете потребовать от пользователя подтверждения пароля перед выполнением действия или перед перенаправлением пользователя в конфиденциальный раздел приложения. Laravel содержит встроенный посредник для упрощения этого процесса. Реализация этого функционала потребует от вас определения двух маршрутов: один маршрут для отображения шаблона, предлагающего пользователю подтвердить свой пароль, и другой маршрут для уточнения действительности пароля и дальнейшего перенаправления его к необходимому разделу. -> {tip} В следующей документации обсуждается, как напрямую интегрироваться с функционалом подтверждения пароля Laravel; однако, если вы хотите начать работу быстрее, то [стартовые комплекты Laravel](starter-kits.md) уже включают поддержку этого функционала! +> **Примечание**\ +> В следующей документации обсуждается, как напрямую интегрироваться с функционалом подтверждения пароля Laravel; однако, если вы хотите начать работу быстрее, то [стартовые комплекты Laravel](starter-kits.md) уже включают поддержку этого функционала! ### Конфигурация подтверждения пароля diff --git a/docs/authorization.md b/docs/authorization.md index ee83d0d..4799eee 100644 --- a/docs/authorization.md +++ b/docs/authorization.md @@ -38,7 +38,8 @@ Laravel предлагает два основных способа автори ### Написание шлюзов -> {note} Шлюзы – отличный способ изучить основы функционала авторизации Laravel; однако при создании надежных приложений Laravel, вам следует рассмотреть возможность использования [политик](#creating-policies) для организации ваших правил авторизации. +> **Предупреждение**\ +> Шлюзы – отличный способ изучить основы функционала авторизации Laravel; однако при создании надежных приложений Laravel, вам следует рассмотреть возможность использования [политик](#creating-policies) для организации ваших правил авторизации. Шлюз – это просто замыкание, которое определяет, имеет ли пользователь право выполнять указанное действие. Как правило, шлюзы определяются в методе `boot` поставщика `App\Providers\AuthServiceProvider` с использованием фасада `Gate`. Шлюзы всегда получают экземпляр пользователя в качестве своего первого аргумента и могут получать дополнительные аргументы, например, модель Eloquent. @@ -195,6 +196,33 @@ Laravel предлагает два основных способа автори // Действие разрешено ... + +#### Корректировка статуса HTTP-ответа + +Когда действие запрещено шлюзом, тогда возвращается HTTP-ответ `403`; но иногда необходимо вернуть альтернативный код состояния HTTP. Вы можете изменить код состояния HTTP, возвращаемый при неуспешной авторизации, используя статический метод `denyWithStatus` класса `Illuminate\Auth\Access\Response`: + + use App\Models\User; + use Illuminate\Auth\Access\Response; + use Illuminate\Support\Facades\Gate; + + Gate::define('edit-settings', function (User $user) { + return $user->isAdmin + ? Response::allow() + : Response::denyWithStatus(404); + }); + +Поскольку сокрытие ресурсов приложения с помощью ответа `404` является распространенным шаблоном для веб-приложений, то для удобства предлагается метод `denyAsNotFound`: + + use App\Models\User; + use Illuminate\Auth\Access\Response; + use Illuminate\Support\Facades\Gate; + + Gate::define('edit-settings', function (User $user) { + return $user->isAdmin + ? Response::allow() + : Response::denyAsNotFound(); + }); + ### Хуки шлюзов @@ -308,7 +336,8 @@ php artisan make:policy PostPolicy --model=Post // Возвращаем имя класса политики для переданной модели ... }); -> {note} Любые политики, которые явно отображены в вашем `AuthServiceProvider`, будут иметь **приоритет** над любыми потенциально автоматически обнаруженными политиками. +> **Предупреждение**\ +> Любые политики, которые явно отображены в вашем `AuthServiceProvider`, будут иметь **приоритет** над любыми потенциально автоматически обнаруженными политиками. ## Написание политик @@ -346,7 +375,8 @@ php artisan make:policy PostPolicy --model=Post Если вы использовали опцию `--model` при создании своей политики через Artisan, то она уже будет содержать методы для следующих действий: `viewAny`, `view`, `create`, `update`, `delete`, `restore` и `forceDelete`. -> {tip} Все политики извлекаются через [контейнер служб](container.md) Laravel, что позволяет вам объявлять любые необходимые зависимости в конструкторе политики для их автоматического внедрения. +> **Примечание**\ +> Все политики извлекаются через [контейнер служб](container.md) Laravel, что позволяет вам объявлять любые необходимые зависимости в конструкторе политики для их автоматического внедрения. ### Ответы политики @@ -389,6 +419,49 @@ php artisan make:policy PostPolicy --model=Post // Действие разрешено ... + +#### Корректировка статуса HTTP-ответа + +Когда действие запрещено методом политики, тогда возвращается HTTP-ответ `403`; но иногда необходимо вернуть альтернативный код состояния HTTP. Вы можете изменить код состояния HTTP, возвращаемый при неуспешной авторизации, используя статический метод `denyWithStatus` класса `Illuminate\Auth\Access\Response`: + + use App\Models\Post; + use App\Models\User; + use Illuminate\Auth\Access\Response; + + /** + * Определить, может ли пользователь обновить пост. + * + * @param \App\Models\User $user + * @param \App\Models\Post $post + * @return \Illuminate\Auth\Access\Response + */ + public function update(User $user, Post $post) + { + return $user->id === $post->user_id + ? Response::allow() + : Response::denyWithStatus(404); + } + +Поскольку сокрытие ресурсов приложения с помощью ответа `404` является распространенным шаблоном для веб-приложений, то для удобства предлагается метод `denyAsNotFound`: + + use App\Models\Post; + use App\Models\User; + use Illuminate\Auth\Access\Response; + + /** + * Определить, может ли пользователь обновить пост. + * + * @param \App\Models\User $user + * @param \App\Models\Post $post + * @return \Illuminate\Auth\Access\Response + */ + public function update(User $user, Post $post) + { + return $user->id === $post->user_id + ? Response::allow() + : Response::denyAsNotFound(); + } + ### Методы политики без моделей @@ -455,7 +528,8 @@ php artisan make:policy PostPolicy --model=Post Если вы хотите отклонить все проверки авторизации для определенного типа пользователей, вы можете вернуть `false` из метода `before`. Если возвращается `null`, то проверка авторизации перейдет к методу политики. -> {note} Метод `before` класса политики не будет вызываться, если класс не содержит метода с именем, совпадающим с именем проверяемого полномочия. +> **Предупреждение**\ +> Метод `before` класса политики не будет вызываться, если класс не содержит метода с именем, совпадающим с именем проверяемого полномочия. ## Авторизация действий с помощью политик @@ -622,7 +696,8 @@ php artisan make:policy PostPolicy --model=Post | update | update | | destroy | delete | -> {tip} Вы можете использовать команду `make:policy` с параметром `--model`, чтобы сгенерировать класс политики для указанной модели: `php artisan make:policy PostPolicy --model=Post`. +> **Примечание**\ +> Вы можете использовать команду `make:policy` с параметром `--model`, чтобы сгенерировать класс политики для указанной модели: `php artisan make:policy PostPolicy --model=Post`. ### Авторизация действий с помощью политик через посредника diff --git a/docs/billing.md b/docs/billing.md index 986789b..59971d7 100644 --- a/docs/billing.md +++ b/docs/billing.md @@ -31,7 +31,8 @@ - [Checking Subscription Status](#checking-subscription-status) - [Changing Prices](#changing-prices) - [Subscription Quantity](#subscription-quantity) - - [Multiprice Subscriptions](#multiprice-subscriptions) + - [Subscriptions With Multiple Products](#subscriptions-with-multiple-products) + - [Multiple Subscriptions](#multiple-subscriptions) - [Metered Billing](#metered-billing) - [Subscription Taxes](#subscription-taxes) - [Subscription Anchor Date](#subscription-anchor-date) @@ -54,6 +55,7 @@ - [Single Charge Checkouts](#single-charge-checkouts) - [Subscription Checkouts](#subscription-checkouts) - [Collecting Tax IDs](#collecting-tax-ids) + - [Guest Checkouts](#guest-checkouts) - [Invoices](#invoices) - [Retrieving Invoices](#retrieving-invoices) - [Upcoming Invoices](#upcoming-invoices) @@ -76,7 +78,8 @@ When upgrading to a new version of Cashier, it's important that you carefully review [the upgrade guide](https://github.com/laravel/cashier-stripe/blob/master/UPGRADE.md). -> {note} To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 13 utilizes Stripe API version `2020-08-27`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. +> **Warning** +> To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 14 utilizes Stripe API version `2022-11-15`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. ## Installation @@ -87,7 +90,8 @@ First, install the Cashier package for Stripe using the Composer package manager composer require laravel/cashier ``` -> {note} To ensure Cashier properly handles all Stripe events, remember to [set up Cashier's webhook handling](#handling-stripe-webhooks). +> **Warning** +> To ensure Cashier properly handles all Stripe events, remember to [set up Cashier's webhook handling](#handling-stripe-webhooks). ### Database Migrations @@ -118,7 +122,8 @@ If you would like to prevent Cashier's migrations from running entirely, you may Cashier::ignoreMigrations(); } -> {note} Stripe recommends that any column used for storing Stripe identifiers should be case-sensitive. Therefore, you should ensure the column collation for the `stripe_id` column is set to `utf8_bin` when using MySQL. More information regarding this can be found in the [Stripe documentation](https://stripe.com/docs/upgrades#what-changes-does-stripe-consider-to-be-backwards-compatible). +> **Warning** +> Stripe recommends that any column used for storing Stripe identifiers should be case-sensitive. Therefore, you should ensure the column collation for the `stripe_id` column is set to `utf8_bin` when using MySQL. More information regarding this can be found in the [Stripe documentation](https://stripe.com/docs/upgrades#what-changes-does-stripe-consider-to-be-backwards-compatible). ## Configuration @@ -150,7 +155,8 @@ Cashier assumes your billable model will be the `App\Models\User` class that shi Cashier::useCustomerModel(User::class); } -> {note} If you're using a model other than Laravel's supplied `App\Models\User` model, you'll need to publish and alter the [Cashier migrations](#installation) provided to match your alternative model's table name. +> **Warning** +> If you're using a model other than Laravel's supplied `App\Models\User` model, you'll need to publish and alter the [Cashier migrations](#installation) provided to match your alternative model's table name. ### API Keys @@ -163,7 +169,8 @@ STRIPE_SECRET=your-stripe-secret STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret ``` -> {note} You should ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is defined in your application's `.env` file, as this variable is used to ensure that incoming webhooks are actually from Stripe. +> **Warning** +> You should ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is defined in your application's `.env` file, as this variable is used to ensure that incoming webhooks are actually from Stripe. ### Currency Configuration @@ -180,7 +187,8 @@ In addition to configuring Cashier's currency, you may also specify a locale to CASHIER_CURRENCY_LOCALE=nl_BE ``` -> {note} In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. +> **Warning** +> In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. ### Tax Configuration @@ -203,7 +211,8 @@ Once tax calculation has been enabled, any new subscriptions and any one-off inv For this feature to work properly, your customer's billing details, such as the customer's name, address, and tax ID, need to be synced to Stripe. You may use the [customer data synchronization](#syncing-customer-data-with-stripe) and [Tax ID](#tax-ids) methods offered by Cashier to accomplish this. -> {note} Unfortunately, for now, no tax is calculated for [single charges](#single-charges) or [single charge checkouts](#single-charge-checkouts). In addition, Stripe Tax is currently "invite-only" during its beta period. You can request access to Stripe Tax via the [Stripe Tax website](https://stripe.com/tax#request-access). +> **Warning** +> No tax is calculated for [single charges](#single-charges) or [single charge checkouts](#single-charge-checkouts). ### Logging @@ -289,13 +298,13 @@ Stripe allows you to credit or debit a customer's "balance". Later, this balance $balance = $user->balance(); -To credit a customer's balance, you may provide a negative value to the `applyBalance` method. If you wish, you may also provide a description: +To credit a customer's balance, you may provide a value to the `creditBalance` method. If you wish, you may also provide a description: - $user->applyBalance(-500, 'Premium customer top-up.'); + $user->creditBalance(500, 'Premium customer top-up.'); -Providing a positive value to the `applyBalance` method will debit the customer's balance: +Providing a value to the `debitBalance` method will debit the customer's balance: - $user->applyBalance(300, 'Bad usage penalty.'); + $user->debitBalance(300, 'Bad usage penalty.'); The `applyBalance` method will create new customer balance transactions for the customer. You may retrieve these transaction records using the `balanceTransactions` method, which may be useful in order to provide a log of credits and debits for the customer to review: @@ -368,7 +377,7 @@ You may customize the columns used for syncing customer information to Stripe by return $this->company_name; } -Similarly, you may override the `stripeEmail`, `stripePhone`, and `stripeAddress` methods. These methods will sync information to their corresponding customer parameters when [updating the Stripe customer object](https://stripe.com/docs/api/customers/update). If you wish to take total control over the customer information sync process, you may override the `syncStripeCustomerDetails` method. +Similarly, you may override the `stripeEmail`, `stripePhone`, `stripeAddress`, and `stripePreferredLocales` methods. These methods will sync information to their corresponding customer parameters when [updating the Stripe customer object](https://stripe.com/docs/api/customers/update). If you wish to take total control over the customer information sync process, you may override the `syncStripeCustomerDetails` method. ### Billing Portal @@ -399,7 +408,7 @@ If you would like to generate the URL to the billing portal without generating a ### Storing Payment Methods -In order to create subscriptions or perform "one off" charges with Stripe, you will need to store a payment method and retrieve its identifier from Stripe. The approach used to accomplish this differs based on whether you plan to use the payment method for subscriptions or single charges, so we will examine both below. +In order to create subscriptions or perform "one-off" charges with Stripe, you will need to store a payment method and retrieve its identifier from Stripe. The approach used to accomplish this differs based on whether you plan to use the payment method for subscriptions or single charges, so we will examine both below. #### Payment Methods For Subscriptions @@ -465,7 +474,8 @@ cardButton.addEventListener('click', async (e) => { After the card has been verified by Stripe, you may pass the resulting `setupIntent.payment_method` identifier to your Laravel application, where it can be attached to the customer. The payment method can either be [added as a new payment method](#adding-payment-methods) or [used to update the default payment method](#updating-the-default-payment-method). You can also immediately use the payment method identifier to [create a new subscription](#creating-subscriptions). -> {tip} If you would like more information about Setup Intents and gathering customer payment details please [review this overview provided by Stripe](https://stripe.com/docs/payments/save-and-reuse#php). +> **Note** +> If you would like more information about Setup Intents and gathering customer payment details please [review this overview provided by Stripe](https://stripe.com/docs/payments/save-and-reuse#php). #### Payment Methods For Single Charges @@ -572,7 +582,8 @@ To sync your default payment method information with the customer's default paym $user->updateDefaultPaymentMethodFromStripe(); -> {note} The default payment method on a customer can only be used for invoicing and creating new subscriptions. Due to limitations imposed by Stripe, it may not be used for single charges. +> **Warning** +> The default payment method on a customer can only be used for invoicing and creating new subscriptions. Due to limitations imposed by Stripe, it may not be used for single charges. ### Adding Payment Methods @@ -581,7 +592,8 @@ To add a new payment method, you may call the `addPaymentMethod` method on the b $user->addPaymentMethod($paymentMethod); -> {tip} To learn how to retrieve payment method identifiers please review the [payment method storage documentation](#storing-payment-methods). +> **Note** +> To learn how to retrieve payment method identifiers please review the [payment method storage documentation](#storing-payment-methods). ### Deleting Payment Methods @@ -602,7 +614,8 @@ By default, this method will delete payment methods of the `card` type. To delet $user->deletePaymentMethods('sepa_debit'); -> {note} If a user has an active subscription, your application should not allow them to delete their default payment method. +> **Warning** +> If a user has an active subscription, your application should not allow them to delete their default payment method. ## Subscriptions @@ -628,7 +641,8 @@ The first argument passed to the `newSubscription` method should be the internal The `create` method, which accepts [a Stripe payment method identifier](#storing-payment-methods) or Stripe `PaymentMethod` object, will begin the subscription as well as update your database with the billable model's Stripe customer ID and other relevant billing information. -> {note} Passing a payment method identifier directly to the `create` subscription method will also automatically add it to the user's stored payment methods. +> **Warning** +> Passing a payment method identifier directly to the `create` subscription method will also automatically add it to the user's stored payment methods. #### Collecting Recurring Payments Via Invoice Emails @@ -637,7 +651,11 @@ Instead of collecting a customer's recurring payments automatically, you may ins $user->newSubscription('default', 'price_monthly')->createAndSendInvoice(); -The amount of time a customer has to pay their invoice before their subscription is canceled is determined by your subscription and invoice settings within the [Stripe dashboard](https://dashboard.stripe.com/settings/billing/automatic). +The amount of time a customer has to pay their invoice before their subscription is cancelled is determined by the `days_until_due` option. By default, this is 30 days; however, you may provide a specific value for this option if you wish: + + $user->newSubscription('default', 'price_monthly')->createAndSendInvoice([], [ + 'days_until_due' => 30 + ]); #### Quantities @@ -730,11 +748,11 @@ If you would like to add a subscription to a customer who already has a default #### Creating Subscriptions From The Stripe Dashboard -You may also create subscriptions from the Stripe dashboard itself. When doing so, Cashier will sync newly added subscriptions and assign them a name of `default`. To customize the subscription name that is assigned to dashboard created subscriptions, [extend the `WebhookController`](/docs/{{version}}/billing#defining-webhook-event-handlers) and overwrite the `newSubscriptionName` method. +You may also create subscriptions from the Stripe dashboard itself. When doing so, Cashier will sync newly added subscriptions and assign them a name of `default`. To customize the subscription name that is assigned to dashboard created subscriptions, [extend the `WebhookController`](#defining-webhook-event-handlers) and overwrite the `newSubscriptionName` method. In addition, you may only create one type of subscription via the Stripe dashboard. If your application offers multiple subscriptions that use different names, only one type of subscription may be added through the Stripe dashboard. -Finally, you should always make sure to only add one active subscription per type of subscription offered by your application. If customer has two `default` subscriptions, only the most recently added subscription will be used by Cashier even though both would be synced with your application's database. +Finally, you should always make sure to only add one active subscription per type of subscription offered by your application. If a customer has two `default` subscriptions, only the most recently added subscription will be used by Cashier even though both would be synced with your application's database. ### Checking Subscription Status @@ -803,7 +821,8 @@ The `recurring` method may be used to determine if the user is currently subscri // } -> {note} If a user has two subscriptions with the same name, the most recent subscription will always be returned by the `subscription` method. For example, a user might have two subscription records named `default`; however, one of the subscriptions may be an old, expired subscription, while the other is the current, active subscription. The most recent subscription will always be returned while older subscriptions are kept in the database for historical review. +> **Warning** +> If a user has two subscriptions with the same name, the most recent subscription will always be returned by the `subscription` method. For example, a user might have two subscription records named `default`; however, one of the subscriptions may be an old, expired subscription, while the other is the current, active subscription. The most recent subscription will always be returned while older subscriptions are kept in the database for historical review. #### Canceled Subscription Status @@ -849,7 +868,7 @@ When a subscription has an incomplete payment, you should direct the user to Cas ``` -If you would like the subscription to still be considered active when it's in a `past_due` state, you may use the `keepPastDueSubscriptionsActive` method provided by Cashier. Typically, this method should be called in the `register` method of your `App\Providers\AppServiceProvider`: +If you would like the subscription to still be considered active when it's in a `past_due` or `incomplete` state, you may use the `keepPastDueSubscriptionsActive` and `keepIncompleteSubscriptionsActive` methods provided by Cashier. Typically, these methods should be called in the `register` method of your `App\Providers\AppServiceProvider`: use Laravel\Cashier\Cashier; @@ -861,9 +880,11 @@ If you would like the subscription to still be considered active when it's in a public function register() { Cashier::keepPastDueSubscriptionsActive(); + Cashier::keepIncompleteSubscriptionsActive(); } -> {note} When a subscription is in an `incomplete` state it cannot be changed until the payment is confirmed. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in an `incomplete` state. +> **Warning** +> When a subscription is in an `incomplete` state it cannot be changed until the payment is confirmed. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in an `incomplete` state. #### Subscription Scopes @@ -924,7 +945,8 @@ By default, Stripe prorates charges when swapping between prices. The `noProrate For more information on subscription proration, consult the [Stripe documentation](https://stripe.com/docs/billing/subscriptions/prorations). -> {note} Executing the `noProrate` method before the `swapAndInvoice` method will have no effect on proration. An invoice will always be issued. +> **Warning** +> Executing the `noProrate` method before the `swapAndInvoice` method will have no effect on proration. An invoice will always be issued. ### Subscription Quantity @@ -955,19 +977,19 @@ The `noProrate` method may be used to update the subscription's quantity without For more information on subscription quantities, consult the [Stripe documentation](https://stripe.com/docs/subscriptions/quantities). - -#### Multiprice Subscription Quantities + +#### Quantities For Subscriptions With Multiple Products -If your subscription is a [multiprice subscription](#multiprice-subscriptions), you should pass the name of the price whose quantity you wish to increment or decrement as the second argument to the increment / decrement methods: +If your subscription is a [subscription with multiple products](#subscriptions-with-multiple-products), you should pass the ID of the price whose quantity you wish to increment or decrement as the second argument to the increment / decrement methods: $user->subscription('default')->incrementQuantity(1, 'price_chat'); - -### Multiprice Subscriptions + +### Subscriptions With Multiple Products -[Multiprice subscriptions](https://stripe.com/docs/billing/subscriptions/multiple-products) allow you to assign multiple billing prices to a single subscription. For example, imagine you are building a customer service "helpdesk" application that has a base subscription price of $10 per month but offers a live chat add-on price for an additional $15 per month. Multiprice subscription information is stored in Cashier's `subscription_items` database table. +[Subscription with multiple products](https://stripe.com/docs/billing/subscriptions/multiple-products) allow you to assign multiple billing products to a single subscription. For example, imagine you are building a customer service "helpdesk" application that has a base subscription price of $10 per month but offers a live chat add-on product for an additional $15 per month. Information for subscriptions with multiple products is stored in Cashier's `subscription_items` database table. -You may specify multiple prices for a given subscription by passing an array of prices as the second argument to the `newSubscription` method: +You may specify multiple products for a given subscription by passing an array of prices as the second argument to the `newSubscription` method: use Illuminate\Http\Request; @@ -1008,12 +1030,13 @@ You may remove prices from subscriptions using the `removePrice` method: $user->subscription('default')->removePrice('price_chat'); -> {note} You may not remove the last price on a subscription. Instead, you should simply cancel the subscription. +> **Warning** +> You may not remove the last price on a subscription. Instead, you should simply cancel the subscription. #### Swapping Prices -You may also change the prices attached to a multiprice subscription. For example, imagine a customer has a `price_basic` subscription with a `price_chat` add-on price and you want to upgrade the customer from the `price_basic` to the `price_pro` price: +You may also change the prices attached to a subscription with multiple products. For example, imagine a customer has a `price_basic` subscription with a `price_chat` add-on product and you want to upgrade the customer from the `price_basic` to the `price_pro` price: use App\Models\User; @@ -1043,7 +1066,7 @@ If you want to swap a single price on a subscription, you may do so using the `s #### Proration -By default, Stripe will prorate charges when adding or removing prices from a multiprice subscription. If you would like to make a price adjustment without proration, you should chain the `noProrate` method onto your price operation: +By default, Stripe will prorate charges when adding or removing prices from a subscription with multiple products. If you would like to make a price adjustment without proration, you should chain the `noProrate` method onto your price operation: $user->subscription('default')->noProrate()->removePrice('price_chat'); @@ -1060,7 +1083,8 @@ If you would like to update quantities on individual subscription prices, you ma $user->subscription('default')->updateQuantity(10, 'price_chat'); -> {note} When a subscription has multiple prices the `stripe_price` and `quantity` attributes on the `Subscription` model will be `null`. To access the individual price attributes, you should use the `items` relationship available on the `Subscription` model. +> **Warning** +> When a subscription has multiple prices the `stripe_price` and `quantity` attributes on the `Subscription` model will be `null`. To access the individual price attributes, you should use the `items` relationship available on the `Subscription` model. #### Subscription Items @@ -1083,6 +1107,31 @@ You can also retrieve a specific price using the `findItemOrFail` method: $subscriptionItem = $user->subscription('default')->findItemOrFail('price_chat'); + +### Multiple Subscriptions + +Stripe allows your customers to have multiple subscriptions simultaneously. For example, you may run a gym that offers a swimming subscription and a weight-lifting subscription, and each subscription may have different pricing. Of course, customers should be able to subscribe to either or both plans. + +When your application creates subscriptions, you may provide the name of the subscription to the `newSubscription` method. The name may be any string that represents the type of subscription the user is initiating: + + use Illuminate\Http\Request; + + Route::post('/swimming/subscribe', function (Request $request) { + $request->user()->newSubscription('swimming') + ->price('price_swimming_monthly') + ->create($request->paymentMethodId); + + // ... + }); + +In this example, we initiated a monthly swimming subscription for the customer. However, they may want to swap to a yearly subscription at a later time. When adjusting the customer's subscription, we can simply swap the price on the `swimming` subscription: + + $user->subscription('swimming')->swap('price_swimming_yearly'); + +Of course, you may also cancel the subscription entirely: + + $user->subscription('swimming')->cancel(); + ### Metered Billing @@ -1166,7 +1215,8 @@ For a full reference of all usage data returned and how to use Stripe's cursor b ### Subscription Taxes -> {note} Instead of calculating Tax Rates manually, you can [automatically calculate taxes using Stripe Tax](#tax-configuration) +> **Warning** +> Instead of calculating Tax Rates manually, you can [automatically calculate taxes using Stripe Tax](#tax-configuration) To specify the tax rates a user pays on a subscription, you should implement the `taxRates` method on your billable model and return an array containing the Stripe tax rate IDs. You can define these tax rates in [your Stripe dashboard](https://dashboard.stripe.com/test/tax-rates): @@ -1182,7 +1232,7 @@ To specify the tax rates a user pays on a subscription, you should implement the The `taxRates` method enables you to apply a tax rate on a customer-by-customer basis, which may be helpful for a user base that spans multiple countries and tax rates. -If you're offering multiprice subscriptions, you may define different tax rates for each price by implementing a `priceTaxRates` method on your billable model: +If you're offering subscriptions with multiple products, you may define different tax rates for each price by implementing a `priceTaxRates` method on your billable model: /** * The tax rates that should apply to the customer's subscriptions. @@ -1196,7 +1246,8 @@ If you're offering multiprice subscriptions, you may define different tax rates ]; } -> {note} The `taxRates` method only applies to subscription charges. If you use Cashier to make "one off" charges, you will need to manually specify the tax rate at that time. +> **Warning** +> The `taxRates` method only applies to subscription charges. If you use Cashier to make "one-off" charges, you will need to manually specify the tax rate at that time. #### Syncing Tax Rates @@ -1205,7 +1256,7 @@ When changing the hard-coded tax rate IDs returned by the `taxRates` method, the $user->subscription('default')->syncTaxRates(); -This will also sync any multiprice subscription item tax rates. If your application is offering multiprice subscriptions, you should ensure that your billable model implements the `priceTaxRates` method [discussed above](#subscription-taxes). +This will also sync any item tax rates for a subscription with multiple products. If your application is offering subscriptions with multiple products, you should ensure that your billable model implements the `priceTaxRates` method [discussed above](#subscription-taxes). #### Tax Exemption @@ -1220,7 +1271,8 @@ Cashier also offers the `isNotTaxExempt`, `isTaxExempt`, and `reverseChargeAppli $user->isNotTaxExempt(); $user->reverseChargeApplies(); -> {note} These methods are also available on any `Laravel\Cashier\Invoice` object. However, when invoked on an `Invoice` object, the methods will determine the exemption status at the time the invoice was created. +> **Warning** +> These methods are also available on any `Laravel\Cashier\Invoice` object. However, when invoked on an `Invoice` object, the methods will determine the exemption status at the time the invoice was created. ### Subscription Anchor Date @@ -1301,7 +1353,8 @@ If you would like to offer trial periods to your customers while still collectin This method will set the trial period ending date on the subscription record within the database and instruct Stripe to not begin billing the customer until after this date. When using the `trialDays` method, Cashier will overwrite any default trial period configured for the price in Stripe. -> {note} If the customer's subscription is not canceled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date. +> **Warning** +> If the customer's subscription is not canceled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date. The `trialUntil` method allows you to provide a `DateTime` instance that specifies when the trial period should end: @@ -1352,7 +1405,8 @@ If you would like to offer trial periods without collecting the user's payment m 'trial_ends_at' => now()->addDays(10), ]); -> {note} Be sure to add a [date cast](/docs/{{version}}/eloquent-mutators##date-casting) for the `trial_ends_at` attribute within your billable model's class definition. +> **Warning** +> Be sure to add a [date cast](/docs/{{version}}/eloquent-mutators##date-casting) for the `trial_ends_at` attribute within your billable model's class definition. Cashier refers to this type of trial as a "generic trial", since it is not attached to any existing subscription. The `onTrial` method on the billable model instance will return `true` if the current date is not past the value of `trial_ends_at`: @@ -1400,7 +1454,8 @@ The `extendTrial` method allows you to extend the trial period of a subscription ## Handling Stripe Webhooks -> {tip} You may use [the Stripe CLI](https://stripe.com/docs/stripe-cli) to help test webhooks during local development. +> **Note** +> You may use [the Stripe CLI](https://stripe.com/docs/stripe-cli) to help test webhooks during local development. Stripe can notify your application of a variety of events via webhooks. By default, a route that points to Cashier's webhook controller is automatically registered by the Cashier service provider. This controller will handle all incoming webhook requests. @@ -1413,6 +1468,7 @@ To ensure your application can handle Stripe webhooks, be sure to configure the - `customer.subscription.deleted` - `customer.updated` - `customer.deleted` +- `invoice.payment_succeeded` - `invoice.payment_action_required` For convenience, Cashier includes a `cashier:webhook` Artisan command. This command will create a webhook in Stripe that listens to all of the events required by Cashier: @@ -1439,7 +1495,8 @@ After creation, the webhook will be immediately active. If you wish to create th php artisan cashier:webhook --disabled ``` -> {note} Make sure you protect incoming Stripe webhook requests with Cashier's included [webhook signature verification](#verifying-webhook-signatures) middleware. +> **Warning** +> Make sure you protect incoming Stripe webhook requests with Cashier's included [webhook signature verification](#verifying-webhook-signatures) middleware. #### Webhooks & CSRF Protection @@ -1546,7 +1603,8 @@ The `charge` method will throw an exception if the charge fails. If the charge i // } -> {note} The `charge` method accepts the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. +> **Warning** +> The `charge` method accepts the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. ### Charge With Invoice @@ -1577,7 +1635,8 @@ Alternatively, you may use the `invoiceFor` method to make a "one-off" charge ag Although the `invoiceFor` method is available for you to use, it is recommended that you use the `invoicePrice` and `tabPrice` methods with pre-defined prices. By doing so, you will have access to better analytics and data within your Stripe dashboard regarding your sales on a per-product basis. -> {note} The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. +> **Warning** +> The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. ### Creating Payment Intents @@ -1608,7 +1667,8 @@ When using the `pay` method, the default payment methods that are enabled within return $payment->client_secret; }); -> {note} The `pay` and `payWith` methods accept the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. +> **Warning** +> The `pay` and `payWith` methods accept the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. ### Refunding Charges @@ -1677,18 +1737,21 @@ You may pass an array of prices to the `previewInvoice` method in order to previ ### Generating Invoice PDFs +Before generating invoice PDFs, you should use Composer to install the Dompdf library, which is the default invoice renderer for Cashier: + +```php +composer require dompdf/dompdf +``` + From within a route or controller, you may use the `downloadInvoice` method to generate a PDF download of a given invoice. This method will automatically generate the proper HTTP response needed to download the invoice: use Illuminate\Http\Request; Route::get('/user/invoice/{invoice}', function (Request $request, $invoiceId) { - return $request->user()->downloadInvoice($invoiceId, [ - 'vendor' => 'Your Company', - 'product' => 'Your Product', - ]); + return $request->user()->downloadInvoice($invoiceId); }); -By default, all data on the invoice is derived from the customer and invoice data stored in Stripe. However, you can customize some of this data by providing an array as the second argument to the `downloadInvoice` method. This array allows you to customize information such as your company and product details: +By default, all data on the invoice is derived from the customer and invoice data stored in Stripe. The filename is based on your `app.name` config value. However, you can customize some of this data by providing an array as the second argument to the `downloadInvoice` method. This array allows you to customize information such as your company and product details: return $request->user()->downloadInvoice($invoiceId, [ 'vendor' => 'Your Company', @@ -1699,7 +1762,7 @@ By default, all data on the invoice is derived from the customer and invoice dat 'email' => 'info@example.com', 'url' => 'https://example.com', 'vendorVat' => 'BE123456789', - ], 'my-invoice'); + ]); The `downloadInvoice` method also allows for a custom filename via its third argument. This filename will automatically be suffixed with `.pdf`: @@ -1779,7 +1842,7 @@ When defining your `success_url` checkout option, you may instruct Stripe to add Route::get('/product-checkout', function (Request $request) { return $request->user()->checkout(['price_tshirt' => 1], [ - 'success_url' => route('checkout-success') . '?session_id={CHECKOUT_SESSION_ID}', + 'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}', 'cancel_url' => route('checkout-cancel'), ]); }); @@ -1814,12 +1877,14 @@ You can also perform a simple charge for an ad-hoc product that has not been cre return $request->user()->checkoutCharge(1200, 'T-Shirt', 5); }); -> {note} When using the `checkoutCharge` method, Stripe will always create a new product and price in your Stripe dashboard. Therefore, we recommend that you create the products up front in your Stripe dashboard and use the `checkout` method instead. +> **Warning** +> When using the `checkoutCharge` method, Stripe will always create a new product and price in your Stripe dashboard. Therefore, we recommend that you create the products up front in your Stripe dashboard and use the `checkout` method instead. ### Subscription Checkouts -> {note} Using Stripe Checkout for subscriptions requires you to enable the `customer.subscription.created` webhook in your Stripe dashboard. This webhook will create the subscription record in your database and store all of the relevant subscription items. +> **Warning** +> Using Stripe Checkout for subscriptions requires you to enable the `customer.subscription.created` webhook in your Stripe dashboard. This webhook will create the subscription record in your database and store all of the relevant subscription items. You may also use Stripe Checkout to initiate subscriptions. After defining your subscription with Cashier's subscription builder methods, you may call the `checkout `method. When a customer visits this route they will be redirected to Stripe's Checkout page: @@ -1855,7 +1920,8 @@ Of course, you can also enable promotion codes for subscription checkouts: ->checkout(); }); -> {note} Unfortunately Stripe Checkout does not support all subscription billing options when starting subscriptions. Using the `anchorBillingCycleOn` method on the subscription builder, setting proration behavior, or setting payment behavior will not have any effect during Stripe Checkout sessions. Please consult [the Stripe Checkout Session API documentation](https://stripe.com/docs/api/checkout/sessions/create) to review which parameters are available. +> **Warning** +> Unfortunately Stripe Checkout does not support all subscription billing options when starting subscriptions. Using the `anchorBillingCycleOn` method on the subscription builder, setting proration behavior, or setting payment behavior will not have any effect during Stripe Checkout sessions. Please consult [the Stripe Checkout Session API documentation](https://stripe.com/docs/api/checkout/sessions/create) to review which parameters are available. #### Stripe Checkout & Trial Periods @@ -1882,7 +1948,39 @@ Checkout also supports collecting a customer's Tax ID. To enable this on a check When this method is invoked, a new checkbox will be available to the customer that allows them to indicate if they're purchasing as a company. If so, they will have the opportunity to provide their Tax ID number. -> {note} If you have already configured [automatic tax collection](#tax-configuration) in your application's service provider then this feature will be enabled automatically and there is no need to invoke the `collectTaxIds` method. +> **Warning** +> If you have already configured [automatic tax collection](#tax-configuration) in your application's service provider then this feature will be enabled automatically and there is no need to invoke the `collectTaxIds` method. + + +### Guest Checkouts + +Using the `Checkout::guest` method, you may initiate checkout sessions for guests of your application that do not have an "account": + + use Illuminate\Http\Request; + use Laravel\Cashier\Checkout; + + Route::get('/product-checkout', function (Request $request) { + return Checkout::guest()->create('price_tshirt', [ + 'success_url' => route('your-success-route'), + 'cancel_url' => route('your-cancel-route'), + ]); + }); + +Similarly to when creating checkout sessions for existing users, you may utilize additional methods available on the `Laravel\Cashier\CheckoutBuilder` instance to customize the guest checkout session: + + use Illuminate\Http\Request; + use Laravel\Cashier\Checkout; + + Route::get('/product-checkout', function (Request $request) { + return Checkout::guest() + ->withPromotionCode('promo-code') + ->create('price_tshirt', [ + 'success_url' => route('your-success-route'), + 'cancel_url' => route('your-cancel-route'), + ]); + }); + +After a guest checkout has been completed, Stripe can dispatch a `checkout.session.completed` webhook event, so make sure to [configure your Stripe webhook](https://dashboard.stripe.com/webhooks) to actually send this event to your application. Once the webhook has been enabled within the Stripe dashboard, you may [handle the webhook with Cashier](#handling-stripe-webhooks). The object contained in the webhook payload will be a [`checkout` object](https://stripe.com/docs/api/checkout/sessions/object) that you may inspect in order to fulfill your customer's order. ## Handling Failed Payments @@ -1953,9 +2051,10 @@ You can derive the specific status of an incomplete payment by inspecting the `p ## Strong Customer Authentication -If your business or one of your customers is based in Europe you will need to abide by the EU's Strong Customer Authentication (SCA) regulations. These regulations were imposed in September 2019 by the European Union to prevent payment fraud. Luckily, Stripe and Cashier are prepared for building SCA compliant applications. +If your business or one of your customers is based in Europe you will need to abide by the EU's Strong Customer Authentication (SCA) regulations. These regulations were imposed in September 2019 by the European Union to prevent payment fraud. Luckily, Stripe and Cashier are prepared for building SCA compliant applications. -> {note} Before getting started, review [Stripe's guide on PSD2 and SCA](https://stripe.com/guides/strong-customer-authentication) as well as their [documentation on the new SCA APIs](https://stripe.com/docs/strong-customer-authentication). +> **Warning** +> Before getting started, review [Stripe's guide on PSD2 and SCA](https://stripe.com/guides/strong-customer-authentication) as well as their [documentation on the new SCA APIs](https://stripe.com/docs/strong-customer-authentication). ### Payments Requiring Additional Confirmation @@ -1982,7 +2081,8 @@ CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment To ensure that off-session payment confirmation notifications are delivered, verify that [Stripe webhooks are configured](#handling-stripe-webhooks) for your application and the `invoice.payment_action_required` webhook is enabled in your Stripe dashboard. In addition, your `Billable` model should also use Laravel's `Illuminate\Notifications\Notifiable` trait. -> {note} Notifications will be sent even when customers are manually making a payment that requires additional confirmation. Unfortunately, there is no way for Stripe to know that the payment was done manually or "off-session". But, a customer will simply see a "Payment Successful" message if they visit the payment page after already confirming their payment. The customer will not be allowed to accidentally confirm the same payment twice and incur an accidental second charge. +> **Warning** +> Notifications will be sent even when customers are manually making a payment that requires additional confirmation. Unfortunately, there is no way for Stripe to know that the payment was done manually or "off-session". But, a customer will simply see a "Payment Successful" message if they visit the payment page after already confirming their payment. The customer will not be allowed to accidentally confirm the same payment twice and incur an accidental second charge. ## Stripe SDK @@ -2018,4 +2118,5 @@ To get started, add the **testing** version of your Stripe secret to your `phpun Now, whenever you interact with Cashier while testing, it will send actual API requests to your Stripe testing environment. For convenience, you should pre-fill your Stripe testing account with subscriptions / prices that you may use during testing. -> {tip} In order to test a variety of billing scenarios, such as credit card denials and failures, you may use the vast range of [testing card numbers and tokens](https://stripe.com/docs/testing) provided by Stripe. +> **Note** +> In order to test a variety of billing scenarios, such as credit card denials and failures, you may use the vast range of [testing card numbers and tokens](https://stripe.com/docs/testing) provided by Stripe. diff --git a/docs/blade.md b/docs/blade.md index 7855363..837f543 100644 --- a/docs/blade.md +++ b/docs/blade.md @@ -1,6 +1,7 @@ # Laravel 9 · Шаблонизатор Blade - [Введение](#introduction) + - [Использование Blade с Livewire](#supercharging-blade-with-livewire) - [Отображение данных](#displaying-data) - [Преобразование в HTML-сущности](#html-entity-encoding) - [Blade и JavaScript фреймворки](#blade-and-javascript-frameworks) @@ -10,7 +11,7 @@ - [Циклы](#loops) - [Переменная Loop](#the-loop-variable) - [Условные классы](#conditional-classes) - - [Отмеченные / Выделенные / Отключенные элементы интерфейса](#checked-and-selected) + - [Дополнительные атрибуты](#additional-attributes) - [Подключение дочерних шаблонов](#including-subviews) - [Директива `@once`](#the-once-directive) - [Необработанный PHP](#raw-php) @@ -28,7 +29,7 @@ - [Анонимные составные компоненты](#anonymous-index-components) - [Свойства / атрибуты данных](#data-properties-attributes) - [Доступ к данным родительского компонента](#accessing-parent-data) - - [Пространства имен анонимных компонентов](#anonymous-component-namespaces) + - [Пути анонимных компонентов](#anonymous-component-paths) - [Создание макетов](#building-layouts) - [Макеты с использованием компонентов](#layouts-using-components) - [Макеты с использованием наследования шаблонов](#layouts-using-template-inheritance) @@ -39,6 +40,7 @@ - [Стеки](#stacks) - [Внедрение служб](#service-injection) - [Отрисовка встроенных шаблонов Blade](#rendering-inline-blade-templates) +- [Отрисовка фрагментов Blade](#rendering-blade-fragments) - [Расширение Blade](#extending-blade) - [Пользовательские обработчики вывода](#custom-echo-handlers) - [Пользовательские операторы If](#custom-if-statements) @@ -54,7 +56,10 @@ Blade – это простой, но мощный движок шаблонов return view('greeting', ['name' => 'Finn']); }); -> {tip} Хотите вывести использование шаблонов Blade на новый уровень и с легкостью создавать динамические интерфейсы? Ознакомьтесь с [Laravel Livewire](https://laravel-livewire.com). + +### Использование Blade с Livewire + +Хотите вывести использование шаблонов Blade на новый уровень и с легкостью создавать динамические интерфейсы? Ознакомьтесь с [Laravel Livewire](https://laravel-livewire.com). Livewire позволяет вам писать компоненты Blade, которые дополнены динамической функциональностью интерфейсных фреймворков, таких как React или Vue, и обеспечивает отличный подход к созданию современных интерфейсов без сложностей многих фреймворков JavaScript, таких как рендеринг на клиентской стороне или этапов их сборки. ## Отображение данных @@ -71,7 +76,8 @@ Blade – это простой, но мощный движок шаблонов Hello, {{ $name }}. ``` -> {tip} Выражения вывода `{{ }}` Blade автоматически отправляются через функцию `htmlspecialchars` PHP для предотвращения XSS-атак. +> **Примечание**\ +> Выражения вывода `{{ }}` Blade автоматически отправляются через функцию `htmlspecialchars` PHP для предотвращения XSS-атак. Вы не ограничены отображением содержимого переменных, переданных в шаблон. Вы также можете вывести результаты любой функции PHP. Фактически, вы можете поместить любой PHP-код в выражение вывода Blade: @@ -113,7 +119,8 @@ The current UNIX timestamp is {{ time() }}. Hello, {!! $name !!}. ``` -> {note} Будьте очень осторожны при выводе содержимого, полученного от пользователей вашего приложения. Обычно следует использовать экранированный синтаксис двойных фигурных скобок для предотвращения атак XSS при отображении данных, предоставленных пользователем. +> **Предупреждение**\ +> Будьте очень осторожны при выводе содержимого, полученного от пользователей вашего приложения. Обычно следует использовать экранированный синтаксис двойных фигурных скобок для предотвращения атак XSS при отображении данных, предоставленных пользователем. ### Blade и JavaScript фреймворки @@ -165,7 +172,8 @@ Hello, @{{ name }}. ``` -> {note} Вы должны использовать метод `Js::from` только для отображения существующих переменных как JSON. Шаблонизатор Blade основан на регулярных выражениях, и попытки передать сложное выражение в метод могут вызвать неожиданные сбои. +> **Предупреждение**\ +> Вы должны использовать метод `Js::from` только для отображения существующих переменных как JSON. Шаблонизатор Blade основан на регулярных выражениях, и попытки передать сложное выражение в метод могут вызвать неожиданные сбои. #### Директива `@verbatim` @@ -340,7 +348,8 @@ Hello, @{{ name }}. @endwhile ``` -> {tip} При итерации с помощью `foreach` вы можете использовать [переменную Loop](#the-loop-variable), чтобы получить информацию о цикле, например, находитесь ли вы в первой или последней итерации цикла. +> **Примечание**\ +> При итерации с помощью `foreach` вы можете использовать [переменную Loop](#the-loop-variable), чтобы получить информацию о цикле, например, находитесь ли вы в первой или последней итерации цикла. Вы также можете пропустить текущую итерацию или завершить цикл, используя директивы `@continue` и `@break`: @@ -437,8 +446,8 @@ Hello, @{{ name }}. ``` - -### Отмеченные / Выделенные / Отключенные элементы интерфейса + +### Дополнительные атрибуты Для удобства вы можете использовать директиву `@checked`, чтобы указать, должен ли быть «отмечен» HTML-флажок. Эта директива выведет `checked`, если переданное условие является истинным: @@ -467,10 +476,29 @@ Hello, @{{ name }}. ``` +Более того, директива `@readonly` может использоваться для указания, должен ли указанный элемент быть «только для чтения»: + +```blade +isNotAdmin()) /> +``` + +Кроме того, директива `@required` может использоваться для указания, должен ли указанный элемент быть «обязательным»: + +```blade +isAdmin()) /> +``` + ### Подключение дочерних шаблонов -> {tip} Хотя вы можете использовать директиву `@include`, [компоненты](#components) Blade содержат аналогичный функционал и предлагают несколько преимуществ по сравнению с директивой `@include`, например привязку данных и атрибутов. +> **Примечание**\ +> Хотя вы можете использовать директиву `@include`, [компоненты](#components) Blade содержат аналогичный функционал и предлагают несколько преимуществ по сравнению с директивой `@include`, например привязку данных и атрибутов. Директива `@include` Blade позволяет вам включать шаблоны из другого шаблона. Все переменные, доступные для родительского шаблона, будут доступны для включенного шаблона: @@ -510,7 +538,8 @@ Hello, @{{ name }}. @includeFirst(['custom.admin', 'admin'], ['status' => 'complete']) ``` -> {note} Вам следует избегать использования в ваших шаблонах Blade констант `__DIR__` и `__FILE__`, поскольку они будут ссылаться на расположение кешированного, скомпилированного шаблона. +> **Предупреждение**\ +> Вам следует избегать использования в ваших шаблонах Blade констант `__DIR__` и `__FILE__`, поскольку они будут ссылаться на расположение кешированного, скомпилированного шаблона. #### Отрисовка шаблонов с коллекциями @@ -529,7 +558,8 @@ Hello, @{{ name }}. @each('view.name', $jobs, 'job', 'view.empty') ``` -> {note} Шаблоны, отображаемые с помощью `@each`, не наследуют переменные родительского шаблона. Если дочернему шаблону требуются эти переменные, вам следует использовать вместо них директивы `@foreach` и `@include`. +> **Предупреждение**\ +> Шаблоны, отображаемые с помощью `@each`, не наследуют переменные родительского шаблона. Если дочернему шаблону требуются эти переменные, вам следует использовать вместо них директивы `@foreach` и `@include`. ### Директива `@once` @@ -567,6 +597,12 @@ Hello, @{{ name }}. @endphp ``` +Если вам нужно написать только одно выражение PHP, то вы можете включить его в директиву `@php`: + +```blade +@php($counter = 1) +``` + ### Комментарии @@ -595,7 +631,7 @@ php artisan make:component Alert php artisan make:component Forms/Input ``` -Приведенная выше команда создаст компонент `Input` в каталоге `App\View\Components\Forms`, а шаблон будет помещен в каталог `resources/views/components/forms`. +Приведенная выше команда создаст компонент `Input` в каталоге `app/View/Components/Forms`, а шаблон будет помещен в каталог `resources/views/components/forms`. Если вы хотите создать анонимный компонент (компонент только с шаблоном Blade и без класса), то вы можете использовать флаг `--view` при вызове команды `make:component`: @@ -662,7 +698,7 @@ Blade автоматически обнаружит класс, связанны ``` -Если класс компонента имеет вложенность в каталоге `app/View/Components`, то вы можете использовать символ `.` для обозначения вложенности каталогов. Например, если мы предполагаем, что компонент находится в `App\View\Components\Inputs\Button.php`, то мы можем отобразить его так: +Если класс компонента имеет вложенность в каталоге `app/View/Components`, то вы можете использовать символ `.` для обозначения вложенности каталогов. Например, если мы предполагаем, что компонент находится в `app/View/Components/Inputs/Button.php`, то мы можем отобразить его так: ```blade @@ -755,6 +791,19 @@ Blade автоматически обнаружит класс, связанны ``` + +#### Синтаксис кратких атрибутов + +При передаче атрибутов компонентам вы можете использовать синтаксис «кратких атрибутов». Это бывает удобно, поскольку имена атрибутов часто совпадают с именами переменных, которым они соответствуют: + +```blade +{{-- Синтаксис кратких атрибутов ... --}} + + +{{-- Эквивалентно ... --}} + +``` + #### Экранирование атрибутов от синтаксического анализа @@ -793,7 +842,7 @@ Blade отобразит следующий HTML-код: Вы можете выполнить этот метод из своего шаблона компонента, вызвав переменную, соответствующую имени метода: ```blade - ``` @@ -892,7 +941,8 @@ public function __construct(AlertCreator $creator, $type, $message) ``` -> {note} Использование таких директив, как `@env` в тегах компонентов в настоящее время не поддерживается. Например, `` не будет компилироваться. +> **Предупреждение**\ +> Использование таких директив, как `@env` в тегах компонентов в настоящее время не поддерживается. Например, `` не будет компилироваться. #### Атрибуты по умолчанию и слияние атрибутов @@ -938,7 +988,8 @@ public function __construct(AlertCreator $creator, $type, $message) ``` -> {tip} Если вам нужно условно скомпилировать классы для других элементов HTML, которые не должны получать объединенные атрибуты, вы можете использовать [директиву `@class`](#conditional-classes). +> **Примечание**\ +> Если вам нужно условно скомпилировать классы для других элементов HTML, которые не должны получать объединенные атрибуты, вы можете использовать [директиву `@class`](#conditional-classes). #### Слияние неклассовых атрибутов @@ -1173,7 +1224,8 @@ php artisan make:component Alert --inline ### Самостоятельная регистрация компонентов -> {note} Следующая документация по самостоятельной регистрации компонентов в первую очередь полезна тем, кто пишет пакеты Laravel, которые включают компоненты. Если вы не пишете пакет, эта часть документации компонента может быть вам не нужна. +> **Предупреждение**\ +> Следующая документация по самостоятельной регистрации компонентов в первую очередь полезна тем, кто пишет пакеты Laravel, которые включают компоненты. Если вы не пишете пакет, эта часть документации компонента может быть вам не нужна. При написании компонентов для вашего собственного приложения компоненты автоматически обнаруживаются в каталогах `app/View/Components` и `resources/views/components`. @@ -1326,16 +1378,15 @@ Blade автоматически обнаружит класс, связанны ``` -> {note} Директива `@aware` не может получить доступ к родительским данным, если они не переданы родительскому компоненту явным образом через атрибуты HTML. Значения по умолчанию `@props`, которые не переданы явно родительскому компоненту, не могут быть доступны директиве `@aware`. +> **Предупреждение**\ +> Директива `@aware` не может получить доступ к родительским данным, если они не переданы родительскому компоненту явным образом через атрибуты HTML. Значения по умолчанию `@props`, которые не переданы явно родительскому компоненту, не могут быть доступны директиве `@aware`. -### Анонимные пространства имен компонентов +### Пути анонимных компонентов Как обсуждалось ранее, анонимные компоненты обычно определяются путем размещения шаблона Blade в вашем каталоге `resources/views/components`. Однако иногда требуется зарегистрировать другие пути анонимных компонентов в Laravel в дополнение к пути по умолчанию. -Например, при создании приложения для бронирования отеля вы можете поместить анонимные компоненты, связанные с бронированием авиабилета, в каталог `resources/views/flights/bookings/components`. Чтобы сообщить Laravel о расположении этого анонимного компонента, вы можете использовать метод `anonymousComponentNamespace` фасада Blade. - -Метод `anonymousComponentNamespace` принимает «путь» к местоположению анонимного компонента в качестве первого аргумента и «пространство имен», в котором должны быть размещены компоненты, в качестве второго аргумента. Как вы увидите в приведенном ниже примере, «пространство имен» будет добавлено к имени компонента при отрисовки компонента. Как правило, вызов этого метода осуществляется в методе `boot` одного из [поставщиков служб](providers.md) вашего приложения: +Метод `anonymousComponentPath` принимает «путь» к местоположению анонимного компонента в качестве первого аргумента и необязательное «пространство имен», в котором должны быть размещены компоненты, в качестве второго аргумента. Как правило, вызов этого метода осуществляется в методе `boot` одного из [поставщиков служб](providers.md) вашего приложения: /** * Загрузка любых служб приложения. @@ -1344,13 +1395,23 @@ Blade автоматически обнаружит класс, связанны */ public function boot() { - Blade::anonymousComponentNamespace('flights.bookings.components', 'flights'); + Blade::anonymousComponentPath(__DIR__.'/../components'); } -Учитывая приведенный выше пример, вы можете использовать компонент `panel`, который существует в только что зарегистрированном каталоге компонентов, следующим образом: +Когда пути к компонентам зарегистрированы без указанного префикса, как в приведенном выше примере, тогда они могут быть выведены в компонентах Blade также без соответствующего префикса. Например, если в указанном выше пути существует компонент `panel.blade.php`, то он может отображаться следующим образом: ```blade - + +``` + +Префикс «пространства имен» может быть указан в качестве второго аргумента метода `anonymousComponentPath`: + + Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard'); + +Когда префикс указан, тогда компоненты в этом «пространстве имен» могут быть выведены путем добавления префикса к пространству имен компонента к имени компонента при отображении компонента: + +```blade + ``` @@ -1478,7 +1539,8 @@ Route::get('/tasks', function () { В этом примере секция `sidebar` использует директиву `@@parent` для добавления (а не перезаписи) содержимого к боковой панели макета. Директива `@@parent` будет заменена содержимым макета при визуализации представления. -> {tip} В отличие от предыдущего примера, нынешняя секция `sidebar` заканчивается `@endsection` вместо `@show`. Директива `@endsection` будет только определять секцию, в то время как `@show` будет определять и **немедленно дополнять** секцию. +> **Примечание**\ +> В отличие от предыдущего примера, нынешняя секция `sidebar` заканчивается `@endsection` вместо `@show`. Директива `@endsection` будет только определять секцию, в то время как `@show` будет определять и **немедленно дополнять** секцию. Директива `@yield` также принимает значение по умолчанию в качестве второго параметра. Это значение будет отображено, если дополняемый раздел не определен: @@ -1573,6 +1635,14 @@ Blade позволяет вам добавлять содержимое к им @endpush ``` +Если вы хотите добавить содержимое в `@push`, только при соблюдении условий логического выражения, то вы можете использовать директиву `@pushIf`: + +```blade +@pushIf($shouldPush, 'scripts') + +@endPushIf +``` + Вы можете помещать в стек сколько угодно раз. Чтобы отобразить полное содержимое стека, передайте имя стека в директиву `@stack`: ```blade @@ -1631,6 +1701,27 @@ return Blade::render( ); ``` + +## Отрисовка фрагментов Blade + +При использовании интерфейсных фреймворков, таких как [Turbo](https://turbo.hotwired.dev/) и [htmx](https://htmx.org/), иногда может потребоваться вернуть только часть шаблона Blade в рамках вашего HTTP-ответа. «Фрагменты» Blade позволяют сделать именно это. Для начала поместите часть вашего шаблона Blade в директивы `@fragment` и `@endfragment`: + +```blade +@fragment('user-list') +
    + @foreach ($users as $user) +
  • {{ $user->name }}
  • + @endforeach +
+@endfragment +``` + +Затем при отрисовки представления, использующего этот шаблон, вы можете вызвать метод `fragment` для указания того, что только указанный фрагмент должен быть включен в исходящий HTTP-ответ: + +```php +return view('dashboard', ['users' => $users])->fragment('user-list'); +``` + ## Расширение Blade @@ -1674,7 +1765,8 @@ Blade позволяет вам определять ваши собственн format('m/d/Y H:i'); ?> -> {note} После обновления логики директивы Blade вам нужно будет удалить все кешированные шаблоны Blade. Кешированные шаблоны Blade могут быть удалены с помощью команды `view:clear` Artisan. +> **Предупреждение**\ +> После обновления логики директивы Blade вам нужно будет удалить все кешированные шаблоны Blade. Кешированные шаблоны Blade могут быть удалены с помощью команды `view:clear` Artisan. ### Пользовательские обработчики вывода diff --git a/docs/broadcasting.md b/docs/broadcasting.md index 7f6ad9a..f4548e7 100644 --- a/docs/broadcasting.md +++ b/docs/broadcasting.md @@ -54,7 +54,8 @@ По умолчанию Laravel содержит два серверных драйвера трансляции на выбор: [Pusher Channels](https://pusher.com/channels) и [Ably](https://ably.io). Однако пакеты сообщества, например, [laravel-websockets](https://beyondco.de/docs/laravel-websockets/getting-started/introduction) и [soketi](https://docs.soketi.app/) предлагают дополнительные драйверы трансляции без использования платных провайдеров. -> {tip} Прежде чем ближе ознакомиться с трансляцией событий, убедитесь, что вы прочитали документацию Laravel о [событиях и слушателях](events.md). +> **Примечание**\ +> Прежде чем ближе ознакомиться с трансляцией событий, убедитесь, что вы прочитали документацию Laravel о [событиях и слушателях](events.md). ## Установка на стороне сервера @@ -163,13 +164,14 @@ npm install --save-dev laravel-echo pusher-js ```js import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; -window.Pusher = require('pusher-js'); +window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', - key: process.env.MIX_PUSHER_APP_KEY, - cluster: process.env.MIX_PUSHER_APP_CLUSTER, + key: import.meta.env.VITE_PUSHER_APP_KEY, + cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, forceTLS: true }); ``` @@ -180,7 +182,8 @@ window.Echo = new Echo({ npm run dev ``` -> {tip} Чтобы узнать больше о компиляции JavaScript-исходников вашего приложения, обратитесь к документации [Laravel Mix](mix.md). +> **Примечание**\ +> Чтобы узнать больше о компиляции JavaScript-исходников вашего приложения, обратитесь к документации [Vite](vite.md). #### Использование существующего экземпляра клиента @@ -189,13 +192,16 @@ npm run dev ```js import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; -const client = require('pusher-js'); +const options = { + broadcaster: 'pusher', + key: 'your-pusher-channels-key' +} window.Echo = new Echo({ - broadcaster: 'pusher', - key: 'your-pusher-channels-key', - client: client + ...options, + client: new Pusher(options.key, options) }); ``` @@ -216,12 +222,13 @@ npm install --save-dev laravel-echo pusher-js ```js import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; -window.Pusher = require('pusher-js'); +window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', - key: process.env.MIX_ABLY_PUBLIC_KEY, + key: import.meta.env.VITE_ABLY_PUBLIC_KEY, wsHost: 'realtime-pusher.ably.io', wsPort: 443, disableStats: true, @@ -229,7 +236,7 @@ window.Echo = new Echo({ }); ``` -Обратите внимание, что наша конфигурация Echo для Ably ссылается на переменную окружения `MIX_ABLY_PUBLIC_KEY`. Значение этой переменной должно быть вашим публичным ключом Ably. Ваш публичный ключ – это часть ключа Ably перед символом `:`. +Обратите внимание, что наша конфигурация Echo для Ably ссылается на переменную окружения `VITE_ABLY_PUBLIC_KEY`. Значение этой переменной должно быть вашим публичным ключом Ably. Ваш публичный ключ – это часть ключа Ably перед символом `:`. После того, как вы раскомментировали и настроили конфигурацию Echo в соответствии с вашими потребностями, вы можете скомпилировать исходники вашего приложения: @@ -237,7 +244,8 @@ window.Echo = new Echo({ npm run dev ``` -> {tip} Чтобы узнать больше о компиляции JavaScript-исходников вашего приложения, обратитесь к документации [Laravel Mix](mix.md). +> **Примечание**\ +> Чтобы узнать больше о компиляции JavaScript-исходников вашего приложения, обратитесь к документации [Vite](vite.md). ## Обзор концепции @@ -246,7 +254,8 @@ npm run dev События транслируются по «каналам», которые могут быть публичными или частными. Любой посетитель вашего приложения может подписаться на публичный канал без какой-либо аутентификации или авторизации; однако, чтобы подписаться на частный канал, пользователь должен быть аутентифицирован и авторизован для прослушивания событий на этом канале. -> {tip} Если вы хотите рассмотреть альтернативы Pusher с открытым исходным кодом, то обратитесь к нашей документации по [альтернативам с открытым исходным кодом](#open-source-alternatives). +> **Примечание**\ +> Если вы хотите рассмотреть альтернативы Pusher с открытым исходным кодом, то обратитесь к нашей документации по [альтернативам с открытым исходным кодом](#open-source-alternatives). ### Пример использования @@ -505,7 +514,8 @@ Echo.private(`orders.${orderId}`) public $afterCommit = true; } -> {tip} Чтобы узнать больше о том, как обойти эти проблемы, просмотрите документацию, касающуюся [заданий в очереди и транзакций базы данных](queues.md#jobs-and-database-transactions). +> **Примечание**\ +> Чтобы узнать больше о том, как обойти эти проблемы, просмотрите документацию, касающуюся [заданий в очереди и транзакций базы данных](queues.md#jobs-and-database-transactions). ## Авторизация каналов @@ -552,10 +562,10 @@ window.Echo = new Echo({ channel_name: channel.name }) .then(response => { - callback(false, response.data); + callback(null, response.data); }) .catch(error => { - callback(true, error); + callback(error); }); } }; @@ -587,7 +597,8 @@ window.Echo = new Echo({ return $user->id === $order->user_id; }); -> {note} В отличие от привязки модели к HTTP-маршруту, привязка модели канала не поддерживает [ограничение неявной привязки модели](routing.md#implicit-model-binding-scoping). Однако это редко представляет собой проблему, потому что большинство каналов можно ограничить на основе уникального первичного ключа одной модели. +> **Предупреждение**\ +> В отличие от привязки модели к HTTP-маршруту, привязка модели канала не поддерживает [ограничение неявной привязки модели](routing.md#implicit-model-binding-scoping). Однако это редко представляет собой проблему, потому что большинство каналов можно ограничить на основе уникального первичного ключа одной модели. #### Предварительная аутентификация авторизации канала @@ -647,7 +658,8 @@ php artisan make:channel OrderChannel } } -> {tip} Как и многие другие классы в Laravel, классы каналов будут автоматически разрешены [контейнером служб](container.md). Таким образом, вы можете указать любые зависимости, необходимые для вашего канала, в его конструкторе. +> **Примечание**\ +> Как и многие другие классы в Laravel, классы каналов будут автоматически разрешены [контейнером служб](container.md). Таким образом, вы можете указать любые зависимости, необходимые для вашего канала, в его конструкторе. ## Трансляция событий @@ -678,7 +690,8 @@ axios.post('/task', task) Однако помните, что мы также транслируем создание задачи. Если ваше JavaScript-приложение также прослушивает это событие, чтобы добавить задачи в список задач, у вас будут дублирующиеся задачи в вашем списке: одна из конечной точки и одна из трансляции. Вы можете решить эту проблему, используя метод `toOthers`, чтобы указать вещателю не транслировать событие текущему пользователю. -> {note} Ваше событие должно использовать трейт `Illuminate\Broadcasting\InteractsWithSockets` для вызова метода `toOthers`. +> **Предупреждение**\ +> Ваше событие должно использовать трейт `Illuminate\Broadcasting\InteractsWithSockets` для вызова метода `toOthers`. #### Конфигурирование при использовании метода `toOthers` @@ -876,7 +889,8 @@ Echo.join(`chat.${roomId}`) ## Трансляция событий модели -> {note} Прежде чем читать следующую документацию о трансляции событий модели, мы рекомендуем вам ознакомиться с общими концепциями служб трансляции событий модели Laravel, а также с тем, как самостоятельно создавать и прослушивать трансляции событий. +> **Предупреждение**\ +> Прежде чем читать следующую документацию о трансляции событий модели, мы рекомендуем вам ознакомиться с общими концепциями служб трансляции событий модели Laravel, а также с тем, как самостоятельно создавать и прослушивать трансляции событий. Обычно транслируются события, когда [модели Eloquent](eloquent.md) вашего приложения создаются, обновляются или удаляются. Конечно, это можно легко сделать, [определив пользовательские события изменений состояния модели Eloquent](eloquent.md#events) и применив интерфейс `ShouldBroadcast` к этим событиям. @@ -1070,7 +1084,8 @@ Echo.private(`App.Models.User.${this.user.id}`) ## Клиентские события -> {tip} При использовании [Pusher Channels](https://pusher.com/channels) вы должны включить опцию «Client Events» в разделе «App Settings» вашей [панели управления приложения](https://dashboard.pusher.com/) для отправки клиентских событий. +> **Примечание**\ +> При использовании [Pusher Channels](https://pusher.com/channels) вы должны включить опцию «Client Events» в разделе «App Settings» вашей [панели управления приложения](https://dashboard.pusher.com/) для отправки клиентских событий. По желанию можно транслировать событие другим подключенным клиентам, вообще не затрагивая ваше приложение Laravel. Это может быть особенно полезно для таких вещей, как «ввод» уведомлений, когда вы хотите предупредить пользователей вашего приложения о том, что другой пользователь печатает сообщение. diff --git a/docs/cache.md b/docs/cache.md index 9becc0c..3c125c5 100644 --- a/docs/cache.md +++ b/docs/cache.md @@ -50,7 +50,8 @@ $table->integer('expiration'); }); -> {tip} Вы также можете использовать команду `php artisan cache:table` Artisan для генерации миграции с правильной схемой. +> **Примечание**\ +> Вы также можете использовать команду `php artisan cache:table` Artisan для генерации миграции с правильной схемой. #### Предварительная подготовка драйвера на основе Memcached @@ -216,7 +217,8 @@ Cache::forever('key', 'value'); -> {tip} Если вы используете драйвер `memcached`, то элементы, которые хранятся «на постоянной основе», могут быть удалены, когда кеш достигнет предельного размера. +> **Примечание**\ +> Если вы используете драйвер `memcached`, то элементы, которые хранятся «на постоянной основе», могут быть удалены, когда кеш достигнет предельного размера. ### Удаление элементов из кеша @@ -235,7 +237,8 @@ Cache::flush(); -> {note} Очистка кеша не учитывает ваш настроенный «префикс» кеша и удаляет все записи из кеша. Внимательно учитывайте это при очистке кеша, который используется другими приложениями. +> **Предупреждение**\ +> Очистка кеша не учитывает ваш настроенный «префикс» кеша и удаляет все записи из кеша. Внимательно учитывайте это при очистке кеша, который используется другими приложениями. ### Глобальный помощник кеша @@ -256,17 +259,19 @@ return DB::table('users')->get(); }); -> {tip} При тестировании вызова глобальной функции `cache` вы можете использовать метод `Cache::shouldReceive` так же, как если бы вы [тестировали фасад](mocking.md#mocking-facades). +> **Примечание**\ +> При тестировании вызова глобальной функции `cache` вы можете использовать метод `Cache::shouldReceive` так же, как если бы вы [тестировали фасад](mocking.md#mocking-facades). ## Тегированный кеш -> {note} Теги кеширования не поддерживаются при использовании драйверов кеширования `file`, `dynamodb` или `database`. Более того, при использовании нескольких тегов с кешами, которые хранятся «на постоянной основе», то производительность будет лучше с драйвером, таким как `memcached`, который автоматически очищает устаревшие записи. +> **Предупреждение**\ +> Теги кеширования не поддерживаются при использовании драйверов кеширования `file`, `dynamodb` или `database`. Более того, при использовании нескольких тегов с кешами, которые хранятся «на постоянной основе», то производительность будет лучше с драйвером, таким как `memcached`, который автоматически очищает устаревшие записи. ### Сохранение элементов тегированного кеша -Теги кеша позволяют помечать связанные элементы в кеше, а затем сбрасывать все кешированные значения, которым был назначен данный тег. Вы можете получить доступ к тегированному кешу, передав упорядоченный массив имен тегов. Например, давайте обратимся к тегированному кешу и поместим значение в кеш: +Теги кеша позволяют помечать связанные элементы в кеше, а затем сбрасывать все кешированные значения, которым был назначен данный тег. Вы можете получить доступ к тегированному кешу, передав упорядоченный массив имен тегов. Доступ к элементам, хранящимся с помощью тегов, невозможен без указания тегов, которые использовались для хранения значения. Например, давайте обратимся к тегированному кешу и поместим значение в кеш: Cache::tags(['people', 'artists'])->put('John', $john, $seconds); @@ -295,7 +300,8 @@ ## Атомарные блокировки -> {note} Чтобы использовать этот функционал, ваше приложение должно использовать драйвер кеша `memcached`, `redis`, `dynamodb`, `database`, `file` или `array` в качестве драйвера кеша по умолчанию для вашего приложения. Кроме того, все серверы должны взаимодействовать с одним и тем же центральным сервером кеширования. +> **Предупреждение**\ +> Чтобы использовать этот функционал, ваше приложение должно использовать драйвер кеша `memcached`, `redis`, `dynamodb`, `database`, `file` или `array` в качестве драйвера кеша по умолчанию для вашего приложения. Кроме того, все серверы должны взаимодействовать с одним и тем же центральным сервером кеширования. ### Предварительная подготовка драйверов @@ -411,7 +417,8 @@ return Cache::repository(new MongoStore); }); -> {tip} Если вам интересно, где разместить свой собственный код драйвера кеша, то вы можете создать пространство имен `Extensions` в своем каталоге `app`. Однако имейте в виду, что Laravel не имеет жесткой структуры приложения, и вы можете организовать свое приложение в соответствии со своими предпочтениями. +> **Примечание**\ +> Если вам интересно, где разместить свой собственный код драйвера кеша, то вы можете создать пространство имен `Extensions` в своем каталоге `app`. Однако имейте в виду, что Laravel не имеет жесткой структуры приложения, и вы можете организовать свое приложение в соответствии со своими предпочтениями. ### Регистрация драйвера кеша @@ -462,25 +469,34 @@ Чтобы выполнить код для каждой операции с кешем, вы можете прослушивать [события](events.md), запускаемые кешем. Как правило, регистрация слушателей этих событий осуществляется в поставщике `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 = [ - 'Illuminate\Cache\Events\CacheHit' => [ - 'App\Listeners\LogCacheHit', + CacheHit::class => [ + LogCacheHit::class, ], - 'Illuminate\Cache\Events\CacheMissed' => [ - 'App\Listeners\LogCacheMissed', + CacheMissed::class => [ + LogCacheMissed::class, ], - 'Illuminate\Cache\Events\KeyForgotten' => [ - 'App\Listeners\LogKeyForgotten', + KeyForgotten::class => [ + LogKeyForgotten::class, ], - 'Illuminate\Cache\Events\KeyWritten' => [ - 'App\Listeners\LogKeyWritten', + KeyWritten::class => [ + LogKeyWritten::class, ], ]; diff --git a/docs/cashier-paddle.md b/docs/cashier-paddle.md index 6f7fe88..0a61cfb 100644 --- a/docs/cashier-paddle.md +++ b/docs/cashier-paddle.md @@ -26,6 +26,7 @@ - [Changing Plans](#changing-plans) - [Subscription Quantity](#subscription-quantity) - [Subscription Modifiers](#subscription-modifiers) + - [Multiple Subscriptions](#multiple-subscriptions) - [Pausing Subscriptions](#pausing-subscriptions) - [Cancelling Subscriptions](#cancelling-subscriptions) - [Subscription Trials](#subscription-trials) @@ -48,7 +49,7 @@ [Laravel Cashier Paddle](https://github.com/laravel/cashier-paddle) provides an expressive, fluent interface to [Paddle's](https://paddle.com) subscription billing services. It handles almost all of the boilerplate subscription billing code you are dreading. In addition to basic subscription management, Cashier can handle: coupons, swapping subscription, subscription "quantities", cancellation grace periods, and more. -While working with Cashier we recommend you also review Paddle's [user guides](https://developer.paddle.com/guides) and [API documentation](https://developer.paddle.com/api-reference/intro). +While working with Cashier we recommend you also review Paddle's [user guides](https://developer.paddle.com/guides) and [API documentation](https://developer.paddle.com/api-reference). ## Upgrading Cashier @@ -64,7 +65,8 @@ First, install the Cashier package for Paddle using the Composer package manager composer require laravel/cashier-paddle ``` -> {note} To ensure Cashier properly handles all Paddle events, remember to [set up Cashier's webhook handling](#handling-paddle-webhooks). +> **Warning** +> To ensure Cashier properly handles all Paddle events, remember to [set up Cashier's webhook handling](#handling-paddle-webhooks). ### Paddle Sandbox @@ -175,7 +177,8 @@ In addition to configuring Cashier's currency, you may also specify a locale to CASHIER_CURRENCY_LOCALE=nl_BE ``` -> {note} In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. +> **Warning** +> In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. ### Overriding Default Models @@ -244,7 +247,8 @@ The Paddle checkout widget is asynchronous. Once the user creates or updates a s For more information on pay links, you may review [the Paddle API documentation on pay link generation](https://developer.paddle.com/api-reference/product-api/pay-links/createpaylink). -> {note} After a subscription state change, the delay for receiving the corresponding webhook is typically minimal but you should account for this in your application by considering that your user's subscription might not be immediately available after completing the checkout. +> **Warning** +> After a subscription state change, the delay for receiving the corresponding webhook is typically minimal but you should account for this in your application by considering that your user's subscription might not be immediately available after completing the checkout. #### Manually Rendering Pay Links @@ -301,7 +305,8 @@ $options = [ Please consult Paddle's [guide on Inline Checkout](https://developer.paddle.com/guides/how-tos/checkout/inline-checkout) as well as their [parameter reference](https://developer.paddle.com/reference/paddle-js/parameters) for further details on the inline checkout's available options. -> {note} If you would like to also use the `passthrough` option when specifying custom options, you should provide a key / value array as its value. Cashier will automatically handle converting the array to a JSON string. In addition, the `customer_id` passthrough option is reserved for internal Cashier usage. +> **Warning** +> If you would like to also use the `passthrough` option when specifying custom options, you should provide a key / value array as its value. Cashier will automatically handle converting the array to a JSON string. In addition, the `customer_id` passthrough option is reserved for internal Cashier usage. #### Manually Rendering An Inline Checkout @@ -431,7 +436,8 @@ You may display the original listed prices (without coupon discounts) using the ``` -> {note} When using the prices API, Paddle only allows applying coupons to one-time purchase products and not to subscription plans. +> **Warning** +> When using the prices API, Paddle only allows applying coupons to one-time purchase products and not to subscription plans. ## Customers @@ -540,7 +546,8 @@ You can also pass an array of metadata using the `withMetadata` method: ->withMetadata(['key' => 'value']) ->create(); -> {note} When providing metadata, please avoid using `subscription_name` as a metadata key. This key is reserved for internal use by Cashier. +> **Warning** +> When providing metadata, please avoid using `subscription_name` as a metadata key. This key is reserved for internal use by Cashier. ### Checking Subscription Status @@ -649,7 +656,8 @@ If you would like subscriptions to still be considered active when they are `pas Cashier::keepPastDueSubscriptionsActive(); } -> {note} When a subscription is in a `past_due` state it cannot be changed until payment information has been updated. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in a `past_due` state. +> **Warning** +> When a subscription is in a `past_due` state it cannot be changed until payment information has been updated. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in a `past_due` state. #### Subscription Scopes @@ -726,12 +734,13 @@ If you would like to swap plans and immediately invoice the user instead of wait $user->subscription('default')->swapAndInvoice($premium = 34567); -> {note} Plans may not be swapped when a trial is active. For additional information regarding this limitation, please see the [Paddle documentation](https://developer.paddle.com/api-reference/subscription-api/users/updateuser#usage-notes). +> **Warning** +> Plans may not be swapped when a trial is active. For additional information regarding this limitation, please see the [Paddle documentation](https://developer.paddle.com/api-reference/subscription-api/users/updateuser#usage-notes). #### Prorations -By default, Paddle prorates charges when swapping between plans. The `noProrate` method may be used to update the subscription's without prorating the charges: +By default, Paddle prorates charges when swapping between plans. The `noProrate` method may be used to update the subscriptions without prorating the charges: $user->subscription('default')->noProrate()->swap($premium = 34567); @@ -803,6 +812,31 @@ Modifiers may be deleted by invoking the `delete` method on a `Laravel\Paddle\Mo $modifier->delete(); + +### Multiple Subscriptions + +Paddle allows your customers to have multiple subscriptions simultaneously. For example, you may run a gym that offers a swimming subscription and a weight-lifting subscription, and each subscription may have different pricing. Of course, customers should be able to subscribe to either or both plans. + +When your application creates subscriptions, you may provide the name of the subscription to the `newSubscription` method. The name may be any string that represents the type of subscription the user is initiating: + + use Illuminate\Http\Request; + + Route::post('/swimming/subscribe', function (Request $request) { + $request->user() + ->newSubscription('swimming', $swimmingMonthly = 12345) + ->create($request->paymentMethodId); + + // ... + }); + +In this example, we initiated a monthly swimming subscription for the customer. However, they may want to swap to a yearly subscription at a later time. When adjusting the customer's subscription, we can simply swap the price on the `swimming` subscription: + + $user->subscription('swimming')->swap($swimmingYearly = 34567); + +Of course, you may also cancel the subscription entirely: + + $user->subscription('swimming')->cancel(); + ### Pausing Subscriptions @@ -822,7 +856,8 @@ To resume a paused a subscription, you may call the `unpause` method on the user $user->subscription('default')->unpause(); -> {note} A subscription cannot be modified while it is paused. If you want to swap to a different plan or update quantities you must resume the subscription first. +> **Warning** +> A subscription cannot be modified while it is paused. If you want to swap to a different plan or update quantities you must resume the subscription first. ### Cancelling Subscriptions @@ -843,7 +878,8 @@ If you wish to cancel a subscription immediately, you may call the `cancelNow` m $user->subscription('default')->cancelNow(); -> {note} Paddle's subscriptions cannot be resumed after cancellation. If your customer wishes to resume their subscription, they will have to subscribe to a new subscription. +> **Warning** +> Paddle's subscriptions cannot be resumed after cancellation. If your customer wishes to resume their subscription, they will have to subscribe to a new subscription. ## Subscription Trials @@ -851,7 +887,8 @@ If you wish to cancel a subscription immediately, you may call the `cancelNow` m ### With Payment Method Up Front -> {note} While trialing and collecting payment method details up front, Paddle prevents any subscription changes such as swapping plans or updating quantities. If you want to allow a customer to swap plans during a trial the subscription must be cancelled and recreated. +> **Warning** +> While trialing and collecting payment method details up front, Paddle prevents any subscription changes such as swapping plans or updating quantities. If you want to allow a customer to swap plans during a trial the subscription must be cancelled and recreated. If you would like to offer trial periods to your customers while still collecting payment method information up front, you should use the `trialDays` method when creating your subscription pay links: @@ -868,7 +905,8 @@ If you would like to offer trial periods to your customers while still collectin This method will set the trial period ending date on the subscription record within your application's database, as well as instruct Paddle to not begin billing the customer until after this date. -> {note} If the customer's subscription is not cancelled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date. +> **Warning** +> If the customer's subscription is not cancelled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date. You may determine if the user is within their trial period using either the `onTrial` method of the user instance or the `onTrial` method of the subscription instance. The two examples below are equivalent: @@ -940,14 +978,15 @@ You may use the `onGenericTrial` method if you wish to know specifically that th // User is within their "generic" trial period... } -> {note} There is no way to extend or modify a trial period on a Paddle subscription after it has been created. +> **Warning** +> There is no way to extend or modify a trial period on a Paddle subscription after it has been created. ## Handling Paddle Webhooks Paddle can notify your application of a variety of events via webhooks. By default, a route that points to Cashier's webhook controller is registered by the Cashier service provider. This controller will handle all incoming webhook requests. -By default, this controller will automatically handle cancelling subscriptions that have too many failed charges ([as defined by your Paddle subscription settings](https://vendors.paddle.com/subscription-settings)), subscription updates, and payment method changes; however, as we'll soon discover, you can extend this controller to handle any Paddle webhook event you like. +By default, this controller will automatically handle cancelling subscriptions that have too many failed charges ([as defined by your Paddle dunning settings](https://vendors.paddle.com/recover-settings#dunning-form-id)), subscription updates, and payment method changes; however, as we'll soon discover, you can extend this controller to handle any Paddle webhook event you like. To ensure your application can handle Paddle webhooks, be sure to [configure the webhook URL in the Paddle control panel](https://vendors.paddle.com/alerts-webhooks). By default, Cashier's webhook controller responds to the `/paddle/webhook` URL path. The full list of all webhooks you should enable in the Paddle control panel are: @@ -957,7 +996,8 @@ To ensure your application can handle Paddle webhooks, be sure to [configure the - Payment Succeeded - Subscription Payment Succeeded -> {note} Make sure you protect incoming requests with Cashier's included [webhook signature verification](/docs/{{version}}/cashier-paddle#verifying-webhook-signatures) middleware. +> **Warning** +> Make sure you protect incoming requests with Cashier's included [webhook signature verification](/docs/{{version}}/cashier-paddle#verifying-webhook-signatures) middleware. #### Webhooks & CSRF Protection @@ -1140,7 +1180,8 @@ You may optionally specify a specific amount to refund as well as a reason for t $receipt->order_id, 5.00, 'Unused product time' ); -> {tip} You can use the `$refundRequestId` as a reference for the refund when contacting Paddle support. +> **Note** +> You can use the `$refundRequestId` as a reference for the refund when contacting Paddle support. ## Receipts @@ -1192,28 +1233,49 @@ Next payment: {{ $nextPayment->amount() }} due on {{ $nextPayment->date()->forma Subscription payments fail for various reasons, such as expired cards or a card having insufficient funds. When this happens, we recommend that you let Paddle handle payment failures for you. Specifically, you may [setup Paddle's automatic billing emails](https://vendors.paddle.com/subscription-settings) in your Paddle dashboard. -Alternatively, you can perform more precise customization by catching the [`subscription_payment_failed`](https://developer.paddle.com/webhook-reference/subscription-alerts/subscription-payment-failed) webhook and enabling the "Subscription Payment Failed" option in the Webhook settings of your Paddle dashboard: +Alternatively, you can perform more precise customization by [listening](/docs/{{version}}/events) for the `subscription_payment_failed` Paddle event via the `WebhookReceived` event dispatched by Cashier. You should also ensure the "Subscription Payment Failed" option is enabled in the Webhook settings of your Paddle dashboard: payload['alert_name'] === 'subscription_payment_failed') { + // Handle the failed subscription payment... + } } } +Once your listener has been defined, you should register it within your application's `EventServiceProvider`: + + [ + PaddleEventListener::class, + ], + ]; + } + ## Testing diff --git a/docs/collections.md b/docs/collections.md index e7d1940..ac30613 100644 --- a/docs/collections.md +++ b/docs/collections.md @@ -31,7 +31,8 @@ $collection = collect([1, 2, 3]); -> {tip} Результаты запросов [Eloquent](eloquent.md) всегда возвращаются как экземпляры `Collection`. +> **Примечание**\ +> Результаты запросов [Eloquent](eloquent.md) всегда возвращаются как экземпляры `Collection`. ### Расширение коллекций @@ -103,6 +104,7 @@ - [combine](#method-combine) - [concat](#method-concat) - [contains](#method-contains) +- [containsOneItem](#method-containsoneitem) - [containsStrict](#method-containsstrict) - [count](#method-count) - [countBy](#method-countBy) @@ -131,6 +133,7 @@ - [get](#method-get) - [groupBy](#method-groupby) - [has](#method-has) +- [hasAny](#method-hasany) - [implode](#method-implode) - [intersect](#method-intersect) - [intersectByKeys](#method-intersectbykeys) @@ -366,7 +369,8 @@ // [1, 2, 3] -> {tip} Метод `collect` особенно полезен, когда у вас есть экземпляр `Enumerable` и вам нужен «не-отложенный» экземпляр коллекции. Так как `collect()` является частью контракта `Enumerable`, вы можете безопасно использовать его для получения экземпляра `Collection`. +> **Примечание**\ +> Метод `collect` особенно полезен, когда у вас есть экземпляр `Enumerable` и вам нужен «не-отложенный» экземпляр коллекции. Так как `collect()` является частью контракта `Enumerable`, вы можете безопасно использовать его для получения экземпляра `Collection`. #### `combine()` @@ -436,12 +440,30 @@ Противоположным методу `contains` является метод [doesntContain](#method-doesntcontain). + +#### `containsOneItem()` + +Метод `containsOneItem` определяет, содержит ли коллекция единственный элемент: + + collect([])->containsOneItem(); + + // false + + collect(['1'])->containsOneItem(); + + // true + + collect(['1', '2'])->containsOneItem(); + + // false + #### `containsStrict()` Этот метод имеет ту же сигнатуру, что и метод [`contains`](#method-contains); однако, все значения сравниваются с использованием «жесткого» сравнения. -> {tip} Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-contains). +> **Примечание**\ +> Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-contains). #### `count()` @@ -551,7 +573,8 @@ // [1, 3, 5] -> {tip} Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-diff). +> **Примечание**\ +> Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-diff). #### `diffAssoc()` @@ -755,7 +778,8 @@ Противоположным методу `except` является метод [only](#method-only). -> {tip} Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-except). +> **Примечание**\ +> Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-except). #### `filter()` @@ -937,7 +961,8 @@ // ['framework' => 'laravel'] -> {note} В отличие от большинства других методов коллекции, `forget` модифицирует коллекцию. +> **Предупреждение**\ +> В отличие от большинства других методов коллекции, `forget` модифицирует коллекцию. #### `forPage()` @@ -1083,6 +1108,21 @@ // false + +#### `hasAny()` + +Метод `hasAny` определяет, существует ли какой-либо из переданных ключей в коллекции: + + $collection = collect(['account_id' => 1, 'product' => 'Desk', 'amount' => 5]); + + $collection->hasAny(['product', 'price']); + + // true + + $collection->hasAny(['name', 'price']); + + // false + #### `implode()` @@ -1103,6 +1143,14 @@ // '1-2-3-4-5' +Вы можете передать замыкание методу `implode`, если хотите отформатировать значения, которые будут объединены: + + $collection->implode(function ($item, $key) { + return strtoupper($item['product']); + }, ', '); + + // DESK, CHAIR + #### `intersect()` @@ -1116,7 +1164,8 @@ // [0 => 'Desk', 2 => 'Chair'] -> {tip} Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-intersect). +> **Примечание**\ +> Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-intersect). #### `intersectByKeys()` @@ -1185,9 +1234,9 @@ ] */ -Вы также можете передать методу замыкание. Замыкание должно возвращать имя для ключа коллекции: +Вы также можете передать методу замыкание. Замыкание должно возвращать значение для ключа коллекции: - $keyed = $collection->keyBy(function ($item) { + $keyed = $collection->keyBy(function ($item, $key) { return strtoupper($item['product_id']); }); @@ -1283,7 +1332,8 @@ // [2, 4, 6, 8, 10] -> {note} Как и большинство других методов коллекции, `map` возвращает новый экземпляр коллекции; он не модифицирует коллекцию. Если вы хотите преобразовать исходную коллекцию, используйте метод [`transform`](#method-transform). +> **Предупреждение**\ +> Как и большинство других методов коллекции, `map` возвращает новый экземпляр коллекции; он не модифицирует коллекцию. Если вы хотите преобразовать исходную коллекцию, используйте метод [`transform`](#method-transform). #### `mapInto()` @@ -1434,7 +1484,7 @@ #### `merge()` -Метод `merge` объединяет переданный массив или коллекцию с исходной коллекцией. Если строковый ключ в переданных элементах соответствует строковому ключу в исходной коллекции, значение переданного элемента перезапишет значение в исходной коллекции: +Метод `merge` объединяет переданный массив или коллекцию с исходной коллекцией. Если строковый ключ в переданных элементах соответствует строковому ключу в исходной коллекции, то значение переданного элемента перезапишет значение в исходной коллекции: $collection = collect(['product_id' => 1, 'price' => 100]); @@ -1444,7 +1494,7 @@ // ['product_id' => 1, 'price' => 200, 'discount' => false] -Если ключи переданных элементов являются числовыми, значения будут добавлены в конец коллекции: +Если ключи переданных элементов являются числовыми, то значения будут добавлены в конец коллекции: $collection = collect(['Desk', 'Chair']); @@ -1543,7 +1593,8 @@ Противоположным методу `only` является метод [except](#method-except). -> {tip} Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-only). +> **Примечание**\ +> Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-only). #### `pad()` @@ -1822,6 +1873,14 @@ Если в экземпляре коллекции меньше элементов, чем запрошено, метод `random` сгенерирует исключение `InvalidArgumentException`. +Метод `random` также принимает замыкание, которое получит текущий экземпляр коллекции: + + $random = $collection->random(fn ($items) => min(10, count($items))); + + $random->all(); + + // [1, 2, 3, 4, 5] - (retrieved randomly) + #### `range()` @@ -2031,35 +2090,6 @@ // [3, 2, 5, 1, 4] - (generated randomly) - -#### `sliding()` - -Метод `sliding` возвращает новую фрагментированную коллекцию, представляющих вид «скользящего окна» элементов: - - $collection = collect([1, 2, 3, 4, 5]); - - $chunks = $collection->sliding(2); - - $chunks->toArray(); - - // [[1, 2], [2, 3], [3, 4], [4, 5]] - -Это особенно полезно в сочетании с методом [`eachSpread`](#method-eachspread): - - $transactions->sliding(2)->eachSpread(function ($previous, $current) { - $current->total = $previous->total + $current->amount; - }); - -При желании вы можете передать второе значение `step`, которое определяет расстояние между первым элементом каждого фрагмента: - - $collection = collect([1, 2, 3, 4, 5]); - - $chunks = $collection->sliding(3, step: 2); - - $chunks->toArray(); - - // [[1, 2, 3], [3, 4, 5]] - #### `skip()` @@ -2098,7 +2128,8 @@ // [3, 4] -> {note} Если указанное значение не найдено или замыкание никогда не возвращает `true`, то метод `skipUntil` вернет пустую коллекцию. +> **Предупреждение**\ +> Если указанное значение не найдено или замыкание никогда не возвращает `true`, то метод `skipUntil` вернет пустую коллекцию. #### `skipWhile()` @@ -2115,7 +2146,8 @@ // [4] -> {note} Если замыкание никогда не возвращает `false`, то метод `skipWhile` вернет пустую коллекцию. +> **Предупреждение**\ +> Если замыкание никогда не возвращает `false`, то метод `skipWhile` вернет пустую коллекцию. #### `slice()` @@ -2140,6 +2172,35 @@ Возвращенный фрагмент по умолчанию сохранит ключи. Если вы не хотите сохранять исходные ключи, вы можете использовать метод [`values`](#method-values), чтобы переиндексировать их. + +#### `sliding()` + +Метод `sliding` возвращает новую фрагментированную коллекцию, представляющих вид «скользящего окна» элементов: + + $collection = collect([1, 2, 3, 4, 5]); + + $chunks = $collection->sliding(2); + + $chunks->toArray(); + + // [[1, 2], [2, 3], [3, 4], [4, 5]] + +Это особенно полезно в сочетании с методом [`eachSpread`](#method-eachspread): + + $transactions->sliding(2)->eachSpread(function ($previous, $current) { + $current->total = $previous->total + $current->amount; + }); + +При желании вы можете передать второе значение `step`, которое определяет расстояние между первым элементом каждого фрагмента: + + $collection = collect([1, 2, 3, 4, 5]); + + $chunks = $collection->sliding(3, step: 2); + + $chunks->toArray(); + + // [[1, 2, 3], [3, 4, 5]] + #### `sole()` @@ -2194,7 +2255,8 @@ Если ваши потребности в сортировке более сложны, вы можете передать замыкание методу `sort` с вашим собственным алгоритмом. Обратитесь к документации PHP по [`uasort`](https://www.php.net/manual/ru/function.uasort.php#refsect1-function.uasort-parameters), который используется внутри метода `sort`. -> {tip} Если вам нужно отсортировать коллекцию вложенных массивов или объектов, то см. методы [`sortBy`](#method-sortby) и [`sortByDesc`](#method-sortbydesc). +> **Примечание**\ +> Если вам нужно отсортировать коллекцию вложенных массивов или объектов, то см. методы [`sortBy`](#method-sortby) и [`sortByDesc`](#method-sortbydesc). #### `sortBy()` @@ -2537,7 +2599,8 @@ // [1, 2] -> {note} Если указанное значение не найдено или замыкание никогда не возвращает `true`, то метод `takeUntil` вернет все элементы коллекции. +> **Предупреждение**\ +> Если указанное значение не найдено или замыкание никогда не возвращает `true`, то метод `takeUntil` вернет все элементы коллекции. #### `takeWhile()` @@ -2554,7 +2617,8 @@ // [1, 2] -> {note} Если замыкание никогда не возвращает `false`, метод `takeWhile` вернет все элементы коллекции. +> **Предупреждение**\ +> Если замыкание никогда не возвращает `false`, метод `takeWhile` вернет все элементы коллекции. #### `tap()` @@ -2598,7 +2662,8 @@ ] */ -> {note} Метод `toArray` также преобразует все вложенные объекты коллекции, которые являются экземпляром `Arrayable`, в массив. Если вы хотите получить необработанный массив, лежащий в основе коллекции, используйте вместо этого метод [`all`](#method-all). +> **Предупреждение**\ +> Метод `toArray` также преобразует все вложенные объекты коллекции, которые являются экземпляром `Arrayable`, в массив. Если вы хотите получить необработанный массив, лежащий в основе коллекции, используйте вместо этого метод [`all`](#method-all). #### `toJson()` @@ -2626,7 +2691,8 @@ // [2, 4, 6, 8, 10] -> {note} В отличие от большинства других методов коллекции, `transform` модифицирует коллекцию. Если вы хотите вместо этого создать новую коллекцию, используйте метод [`map`](#method-map). +> **Предупреждение**\ +> В отличие от большинства других методов коллекции, `transform` модифицирует коллекцию. Если вы хотите вместо этого создать новую коллекцию, используйте метод [`map`](#method-map). #### `undot()` @@ -2641,7 +2707,7 @@ 'address.suburb' => 'Detroit', 'address.state' => 'MI', 'address.postcode' => '48219' - ]) + ]); $person = $person->undot(); @@ -2729,7 +2795,8 @@ Метод `unique` использует «гибкое» сравнение при проверке значений элементов, то есть строка с целым значением будет считаться равной целому числу того же значения. Используйте метод [`uniqueStrict`](#method-uniquestrict) для фильтрации с использованием «жесткого» сравнения. -> {tip} Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-unique). +> **Примечание**\ +> Поведение этого метода изменяется при использовании [коллекций Eloquent](eloquent-collections.md#method-unique). #### `uniqueStrict()` @@ -2977,7 +3044,7 @@ Метод `where` использует «гибкое» сравнение при проверке значений элементов, что означает, что строка с целым значением будет считаться равной целому числу того же значения. Используйте метод [`whereStrict`](#method-wherestrict) для фильтрации с использованием «жесткого» сравнения. -При желании вы можете передать оператор сравнения в качестве второго параметра. +При желании вы можете передать оператор сравнения в качестве второго параметра. Поддерживаются следующие операторы: `===`, `!==`, `!=`, `==`, `=`, `<>`, `>`, `<`, `>=` и `<=`: $collection = collect([ ['name' => 'Jim', 'deleted_at' => '2019-01-01 00:00:00'], @@ -3237,7 +3304,8 @@ ### Введение в отложенные коллекции -> {note} Прежде чем узнать больше об отложенных коллекциях Laravel, потратьте некоторое время на то, чтобы ознакомиться с [генераторами PHP](https://www.php.net/manual/ru/language.generators.overview.php). +> **Предупреждение**\ +> Прежде чем узнать больше об отложенных коллекциях Laravel, потратьте некоторое время на то, чтобы ознакомиться с [генераторами PHP](https://www.php.net/manual/ru/language.generators.overview.php). В дополнении к мощному классу `Collection`, класс `LazyCollection` использует [генераторы](https://www.php.net/manual/ru/language.generators.overview.php) PHP, чтобы вы могли работать с очень большим наборы данных при низком потреблении памяти. @@ -3298,7 +3366,20 @@ Почти все методы, доступные в классе `Collection`, также доступны в классе `LazyCollection`. Оба эти класса реализуют контракт `Illuminate\Support\Enumerable`, который определяет следующие методы: - + - [all](#method-all) - [average](#method-average) @@ -3413,7 +3494,8 @@ -> {note} Методы, которые изменяют коллекцию (такие как `shift`, `pop`, `prepend` и т.д.), **недоступны** в классе `LazyCollection`. +> **Предупреждение**\ +> Методы, которые изменяют коллекцию (такие как `shift`, `pop`, `prepend` и т.д.), **недоступны** в классе `LazyCollection`. ### Методы отложенных коллекций diff --git a/docs/configuration.md b/docs/configuration.md index 07b8c09..f83a7f9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -5,6 +5,7 @@ - [Типы переменных окружения](#environment-variable-types) - [Получение конфигурации окружения](#retrieving-environment-configuration) - [Определение текущего окружения](#determining-the-current-environment) + - [Шифрование файлов окружения](#encrypting-environment-files) - [Доступ к значениям конфигурации](#accessing-configuration-values) - [Кеширование конфигурации](#configuration-caching) - [Режим отладки](#debug-mode) @@ -17,6 +18,21 @@ Конфигурационные файлы позволяют настраивать такие вещи, как информация о подключении к базе данных, информация о почтовом сервере, а также другие основные параметры, например, часовой пояс приложения и ключ шифрования. + +#### Обзор приложения + +Спешите? Вы можете получить краткий обзор конфигурации вашего приложения, драйверов и окружения с помощью команды `about` Artisan: + +```shell +php artisan about +``` + +Если вас интересует только определенный раздел при обзоре приложения, то вы можете отфильтровать этот раздел, используя параметр `--only`: + +```shell +php artisan about --only=environment +``` + ## Конфигурация окружения @@ -28,13 +44,16 @@ Если вы работаете в команде, то вы можете не исключать файл `.env.example` из своего приложения. Размещая значения-заполнители в этот файл, другие разработчики в вашей команде могут четко видеть, какие переменные окружения необходимы для запуска вашего приложения. -> {tip} Любая переменная в вашем файле `.env` может быть переопределена внешними переменными окружения, такими как переменные окружения уровня сервера или системы. +> **Примечание**\ +> Любая переменная в вашем файле `.env` может быть переопределена внешними переменными окружения, такими как переменные окружения уровня сервера или системы. #### Безопасность файлов окружения Ваш файл `.env` не должен быть привязан к системе контроля версий вашего приложения, поскольку каждому разработчику / серверу, использующему ваше приложение, может потребоваться другая конфигурация окружения. Кроме того, это будет угрозой безопасности в случае, если злоумышленник получит доступ к вашему репозиторию системы управления версиями, поскольку любые конфиденциальные учетные данные будут раскрыты. +Однако можно зашифровать файл окружения с помощью [шифрования](#encrypting-environment-files). Зашифрованные файлы окружения можно безопасно размещать в системе контроля версий. + #### Дополнительные файлы окружения @@ -65,7 +84,7 @@ APP_NAME="My Application" ### Получение конфигурации окружения -Все переменные, перечисленные в этом файле, будут загружены в суперглобальную переменную `$_ENV` PHP, когда ваше приложение получит запрос. Однако вы можете использовать помощник `env()` для получения значений из переменных ваших конфигурационных файлов. Фактически, если вы просмотрите файлы конфигурации Laravel, вы заметите, что многие параметры уже используют этот помощник: +Все переменные, перечисленные в файле `.env`, будут загружены в суперглобальную переменную `$_ENV` PHP, когда ваше приложение получит запрос. Однако вы можете использовать функцию `env()` для получения значений из переменных ваших конфигурационных файлов. Фактически, если вы просмотрите файлы конфигурации Laravel, то вы заметите, что многие параметры уже используют эту функцию: 'debug' => env('APP_DEBUG', false), @@ -90,19 +109,84 @@ APP_NAME="My Application" // Окружение либо локальное, либо промежуточное ... } -> {tip} Определение текущего окружения приложения может быть отменено путем определения переменной окружения `APP_ENV` на уровне сервера. +> **Примечание**\ +> Определение текущего окружения приложения может быть отменено путем определения переменной окружения `APP_ENV` на уровне сервера. + + +### Шифрование файлов окружения + +Незашифрованные файлы окружения никогда не должны храниться в системе контроля версиями. Однако Laravel позволяет вам шифровать файлы вашего окружения, чтобы их можно было безопасно добавить в систему контроля версиями вместе с остальной частью вашего приложения. + + +#### Шифрование + +Чтобы зашифровать файл окружения, вы можете использовать команду `env:encrypt`: + +```shell +php artisan env:encrypt +``` + +Выполнение команды `env:encrypt` зашифрует ваш файл `.env` и поместит зашифрованное содержимое в файл `.env.encrypted`. Ключ дешифрования будет выведен по завершению команды и должен храниться в безопасном менеджере паролей. Если вы хотите указать свой собственный ключ шифрования, то вы можете использовать параметр `--key` при вызове команды: + +```shell +php artisan env:encrypt --key=3UVsEgGVK36XN82KKeyLFMhvosbZN1aF +``` + +> **Примечание** +> Длина предоставленного ключа должна соответствовать длине ключа, которая требуется используемым шифром. По умолчанию Laravel будет использовать шифр `AES-256-CBC`, для которого требуется 32-символьный ключ. Вы можете использовать любой шифр, поддерживаемый [шифровальщиком](encryption.md) Laravel, передав параметр `--cipher` при вызове команды. + +Если ваше приложение имеет несколько файлов окружения, например, `.env` и `.env.staging`, то вы можете указать файл окружения, который должен быть зашифрован, указав имя окружения с помощью параметра `--env`: + +```shell +php artisan env:encrypt --env=staging +``` + + +#### Дешифрование + +Чтобы расшифровать файл окружения, вы можете использовать команду `env:decrypt`. Для этой команды требуется ключ дешифрования, который Laravel получит из переменной окружения `LARAVEL_ENV_ENCRYPTION_KEY`: + +```shell +php artisan env:decrypt +``` + +Или ключ может быть передан непосредственно команде с помощью параметра `--key`: + +```shell +php artisan env:decrypt --key=3UVsEgGVK36XN82KKeyLFMhvosbZN1aF +``` + +При вызове команды `env:decrypt`, Laravel расшифровывает содержимое файла `.env.encrypted` и помещает расшифрованное содержимое в файл `.env`. + +Параметр `--cipher` может быть передан команде `env:decrypt`, чтобы использовать собственный шифр: + +```shell +php artisan env:decrypt --key=qUWuNRdfuImXcKxZ --cipher=AES-128-CBC +``` + +Если ваше приложение имеет несколько файлов окружения, например, `.env` и `.env.staging`, то вы можете указать файл окружения, который должен быть расшифрован, указав имя окружения с помощью параметра `--env`: + +```shell +php artisan env:decrypt --env=staging +``` + +Чтобы перезаписать существующий файл окружения, вы можете указать параметр `--force` для команды `env:decrypt`: + +```shell +php artisan env:decrypt --force +``` ## Доступ к значениям конфигурации -Вы можете легко получить доступ к своим значениям конфигурации, используя глобальный помощник `config` из любого места вашего приложения. Доступ к значениям конфигурации можно получить с помощью «точечной нотации», который включает имя файла и параметр, к которому вы хотите получить доступ. Также может быть указано значение по умолчанию, которое будет возвращено, если параметр конфигурации отсутствует: +Вы можете легко получить доступ к своим значениям конфигурации, используя глобальную функцию `config` из любого места вашего приложения. Доступ к значениям конфигурации можно получить с помощью «точечной нотации», который включает имя файла и параметр, к которому вы хотите получить доступ. Также может быть указано значение по умолчанию, которое будет возвращено, если параметр конфигурации отсутствует: $value = config('app.timezone'); // Получить значение по умолчанию, если значение конфигурации не существует ... $value = config('app.timezone', 'Asia/Seoul'); -Чтобы установить значения конфигурации во время выполнения скрипта, передайте массив помощнику `config`: +Чтобы установить значения конфигурации во время выполнения скрипта, передайте массив функции `config`: config(['app.timezone' => 'America/Chicago']); @@ -113,7 +197,8 @@ APP_NAME="My Application" Обычно вы должны запускать команду `php artisan config:cache` как часть процесса развертывания эксплуатационного режима. Команду не следует запускать во время локальной разработки, поскольку конфигурационные параметры часто нужно будет изменять в ходе разработки вашего приложения. -> {note} Если вы выполняете команду `config:cache` в процессе развертывания, то вы должны быть уверены, что вызываете функцию `env()` только из ваших файлов конфигурации. После кэширования конфигурации файл `.env` не будет подгружаться; следовательно, функция `env()` будет возвращать только внешние переменные окружения системного уровня. +> **Предупреждение**\ +> Если вы выполняете команду `config:cache` в процессе развертывания, то вы должны быть уверены, что вызываете функцию `env()` только из ваших файлов конфигурации. После кэширования конфигурации файл `.env` не будет подгружаться; следовательно, функция `env()` будет возвращать только внешние переменные окружения системного уровня. ## Режим отладки @@ -148,7 +233,7 @@ php artisan down --retry=60 #### Обход режима обслуживания -Находясь в режиме обслуживания, вы можете использовать параметр `secret`, чтобы указать токен для обхода режима обслуживания: +Чтобы обойти режим обслуживания с помощью секретного токена, вы можете использовать параметр `secret`, чтобы указать токен для обхода режима обслуживания: ```shell php artisan down --secret="1630542a-246b-4b66-afa1-dd72a4c43515" @@ -162,7 +247,8 @@ https://example.com/1630542a-246b-4b66-afa1-dd72a4c43515 При доступе к этому скрытому маршруту вы будете перенаправлены на маршрут `/` приложения. Как только куки будет отправлен вашему браузеру, вы сможете просматривать приложение в обычном режиме, как если бы оно не находилось в режиме обслуживания. -> {tip} Ваш токен режима обслуживания обычно должен состоять из буквенно-цифровых символов и, при необходимости, тире. Вам следует избегать использования в URL-адресах специфичных символов, таких как `?`. +> **Примечание**\ +> Ваш токен режима обслуживания обычно должен состоять из буквенно-цифровых символов и, при необходимости, тире. Вам следует избегать использования в URL-адресах специфичных символов, таких как `?`. #### Предварительный рендеринг шаблона режима обслуживания @@ -193,7 +279,8 @@ php artisan down --redirect=/ php artisan up ``` -> {tip} Вы можете определить свой шаблон режима обслуживания в `resources/views/errors/503.blade.php`. +> **Примечание**\ +> Вы можете определить свой шаблон режима обслуживания в `resources/views/errors/503.blade.php`. #### Режим обслуживания и очереди diff --git a/docs/console-tests.md b/docs/console-tests.md index c18852f..8d80865 100644 --- a/docs/console-tests.md +++ b/docs/console-tests.md @@ -51,7 +51,7 @@ Laravel позволяет вам легко «имитировать» ввод $this->line('Your name is '.$name.' and you prefer '.$language.'.'); }); -Вы можете протестировать эту команду с помощью следующего теста, который использует методы `expectsQuestion`, `expectsOutput`, `doesntExpectOutput` и `assertExitCode`: +Вы можете протестировать эту команду с помощью следующего теста, который использует методы `expectsQuestion`, `expectsOutput`, `doesntExpectOutput`, `expectsOutputToContain`, `doesntExpectOutputToContain` и `assertExitCode`: /** * Тестирование консольной команды. @@ -65,6 +65,8 @@ Laravel позволяет вам легко «имитировать» ввод ->expectsQuestion('Which language do you prefer?', 'PHP') ->expectsOutput('Your name is Taylor Otwell and you prefer PHP.') ->doesntExpectOutput('Your name is Taylor Otwell and you prefer Ruby.') + ->expectsOutputToContain('Taylor Otwell') + ->doesntExpectOutputToContain('you prefer Ruby') ->assertExitCode(0); } diff --git a/docs/container.md b/docs/container.md index 6d6f10e..64b8dc6 100644 --- a/docs/container.md +++ b/docs/container.md @@ -137,7 +137,8 @@ // ... }); -> {tip} Нет необходимости привязывать классы в контейнере, если они не зависят от каких-либо интерфейсов. Контейнеру не нужно указывать, как создавать эти объекты, поскольку он может автоматически извлекать эти объекты с помощью рефлексии. +> **Примечание**\ +> Нет необходимости привязывать классы в контейнере, если они не зависят от каких-либо интерфейсов. Контейнеру не нужно указывать, как создавать эти объекты, поскольку он может автоматически извлекать эти объекты с помощью рефлексии. #### Связывание одиночек @@ -228,7 +229,9 @@ Иногда у вас может быть класс, который получает некоторые внедренные классы, но также нуждается в примитиве, таком как целое число. Вы можете легко использовать контекстную привязку, чтобы внедрить любое значение, которое может понадобиться вашему классу: - $this->app->when('App\Http\Controllers\UserController') + use App\Http\Controllers\UserController; + + $this->app->when(UserController::class) ->needs('$variableName') ->give($value); @@ -363,13 +366,15 @@ $transistor = $this->app->makeWith(Transistor::class, ['id' => 1]); -Если вы находитесь за пределами поставщика служб и не имеете доступа к переменной `$app`, вы можете использовать [фасад](facades.md) `App` для получения экземпляра класса из контейнера: +Если вы находитесь за пределами поставщика служб и не имеете доступа к переменной `$app`, вы можете использовать [фасад](facades.md) `App` или [глобальный помощник](helpers.md#method-app) `app` для получения экземпляра класса из контейнера: use App\Services\Transistor; use Illuminate\Support\Facades\App; $transistor = App::make(Transistor::class); + $transistor = app(Transistor::class); + Если вы хотите, чтобы сам экземпляр контейнера Laravel был внедрен в класс, извлекаемый контейнером, вы можете указать класс `Illuminate\Container\Container` в конструкторе вашего класса: use Illuminate\Container\Container; diff --git a/docs/contributions.md b/docs/contributions.md index 2eaca7a..f712e18 100644 --- a/docs/contributions.md +++ b/docs/contributions.md @@ -38,6 +38,7 @@ - [Пакет Laravel Horizon](https://github.com/laravel/horizon) - [Пакет Laravel Jetstream](https://github.com/laravel/jetstream) - [Пакет Laravel Passport](https://github.com/laravel/passport) +- [Пакет Laravel Pint](https://github.com/laravel/pint) - [Пакет Laravel Sail](https://github.com/laravel/sail) - [Пакет Laravel Sanctum](https://github.com/laravel/sanctum) - [Пакет Laravel Scout](https://github.com/laravel/scout) @@ -74,7 +75,7 @@ ## Какую ветку выбрать при запросах слияния? -**Все** исправления ошибок следует отправлять в последнюю версию, которая поддерживает исправления ошибок (в настоящее время `8.x`). Исправления ошибок **никогда** не следует отправлять в ветку `master`, если они не исправляют функционал, которой присутствует только в следующем релизе. +**Все** исправления ошибок следует отправлять в последнюю версию, которая поддерживает исправления ошибок (в настоящее время `9.x`). Исправления ошибок **никогда** не следует отправлять в ветку `master`, если они не исправляют функционал, которой присутствует только в следующем релизе. **Минорный** функционал, **полностью обратно совместимый** с текущим релизом, может быть отправлен в последнюю стабильную ветку (в настоящее время `9.x`). diff --git a/docs/controllers.md b/docs/controllers.md index a193433..a07d36c 100644 --- a/docs/controllers.md +++ b/docs/controllers.md @@ -13,6 +13,7 @@ - [Ограничение ресурсных маршрутов](#restful-scoping-resource-routes) - [Локализация URI ресурсов](#restful-localizing-resource-uris) - [Дополнение ресурсных контроллеров](#restful-supplementing-resource-controllers) + - [Контроллеры одиночного ресурса](#singleton-resource-controllers) - [Внедрение зависимостей и контроллеры](#dependency-injection-and-controllers) @@ -32,7 +33,6 @@ namespace App\Http\Controllers; - use App\Http\Controllers\Controller; use App\Models\User; class UserController extends Controller @@ -59,7 +59,8 @@ Когда входящий запрос совпадает с указанным URI маршрута, будет вызван метод `show` класса `App\Http\Controllers\UserController`, и параметры маршрута будут переданы методу. -> {tip} Контроллеры **не требуют** расширения базового класса. Однако у вас не будет доступа к удобным функциям, таким как методы `middleware` и `authorize`. +> **Примечание**\ +> Контроллеры **не требуют** расширения базового класса. Однако у вас не будет доступа к удобным функциям, таким как методы `middleware` и `authorize`. ### Контроллеры одиночного действия @@ -70,7 +71,6 @@ namespace App\Http\Controllers; - use App\Http\Controllers\Controller; use App\Models\User; class ProvisionServer extends Controller @@ -98,7 +98,8 @@ php artisan make:controller ProvisionServer --invokable ``` -> {tip} Заготовки контроллера можно настроить с помощью [публикации заготовок](artisan.md#stub-customization). +> **Примечание**\ +> Заготовки контроллера можно настроить с помощью [публикации заготовок](artisan.md#stub-customization). ## Посредник контроллера @@ -183,6 +184,19 @@ DELETE | `/photos/{photo}` | destroy | photos.destroy return Redirect::route('photos.index'); }); + +#### Программно удаляемые модели + +Как правило, неявная привязка модели не извлекает модели, которые были [программно удалены](eloquent.md#soft-deleting), а вместо этого возвращает HTTP-ответ 404. Тем не менее, вы можете указать Laravel разрешить извлечение таких моделей, вызвав метод `withTrashed` при определении маршрута ресурса: + + use App\Http\Controllers\PhotoController; + + Route::resource('photos', PhotoController::class)->withTrashed(); + +Вызов `withTrashed` без аргументов позволит извлечение таких моделей для маршрутов `show`, `edit` и `update` ресурса. Вы можете указать подмножество этих маршрутов, передав массив методу `withTrashed`: + + Route::resource('photos', PhotoController::class)->withTrashed(['show']); + #### Указание модели ресурса @@ -359,7 +373,83 @@ DELETE | `/comments/{comment}` | destroy | comments.destroy Route::get('/photos/popular', [PhotoController::class, 'popular']); Route::resource('photos', PhotoController::class); -> {tip} Помните, что ваши контроллеры должны быть сосредоточенными. Если вам постоянно требуются методы, выходящие за рамки типичного набора действий с ресурсами, рассмотрите возможность разделения вашего контроллера на два меньших контроллера. +> **Примечание**\ +> Помните, что ваши контроллеры должны быть сосредоточенными. Если вам постоянно требуются методы, выходящие за рамки типичного набора действий с ресурсами, рассмотрите возможность разделения вашего контроллера на два меньших контроллера. + + +### Контроллеры одиночного ресурса + +Иногда ваше приложение будет иметь ресурсы, содержащие только один экземпляр. Например, «профиль» пользователя можно редактировать или обновлять, но у пользователя не может быть более одного «профиля». Точно так же изображение может иметь одну «миниатюру». Эти ресурсы называются «одиночными ресурсами», что означает, что может существовать один и только один экземпляр ресурса. В этих сценариях вы можете зарегистрировать контроллер «одиночного» ресурса: + +```php +use App\Http\Controllers\ProfileController; +use Illuminate\Support\Facades\Route; + +Route::singleton('profile', ProfileController::class); +``` + +Приведенное выше определение одиночного ресурса зарегистрирует следующие маршруты. Как видите, маршруты «создания» не регистрируются для одиночных ресурсов, а зарегистрированные маршруты не принимают идентификатор, поскольку может существовать только один экземпляр ресурса: + +| Метод | URI | Действие | Имя маршрута | +|-----------|-----------------------------------|----------|-----------------| +| GET | `/profile` | show | profile.show | +| GET | `/profile/edit` | edit | profile.edit | +| PUT/PATCH | `/profile` | update | profile.update | + +Одиночные ресурсы также могут быть вложены в стандартный ресурс: + +```php +Route::singleton('photos.thumbnail', ThumbnailController::class); +``` + +В этом примере ресурс `photos` получит все [стандартные ресурсные маршруты](#actions-handled-by-resource-controller); однако ресурс `thumbnail` будет одиночным ресурсом со следующими маршрутами: + +| Метод | URI | Действие | Имя маршрута | +|-----------|----------------------------------|----------|--------------------------| +| GET | `/photos/{photo}/thumbnail` | show | photos.thumbnail.show | +| GET | `/photos/{photo}/thumbnail/edit` | edit | photos.thumbnail.edit | +| PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | + + +#### Расширенные одиночные ресурсы + +Иногда требуется определить маршруты создания и сохранения для одиночного ресурса. Для этого вы можете вызвать метод `creatable` при регистрации маршрутов одиночного ресурса: + +```php +Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable(); +``` + +В этом примере будут зарегистрированы следующие маршруты. Для расширенных одиночных ресурсов также будет зарегистрирован маршрут `DELETE`: + +| Метод | URI | Действие | Имя маршрута | +|-----------|------------------------------------|----------|--------------------------| +| GET | `/photos/{photo}/thumbnail/create` | create | photos.thumbnail.create | +| POST | `/photos/{photo}/thumbnail` | store | photos.thumbnail.store | +| GET | `/photos/{photo}/thumbnail` | show | photos.thumbnail.show | +| GET | `/photos/{photo}/thumbnail/edit` | edit | photos.thumbnail.edit | +| PUT/PATCH | `/photos/{photo}/thumbnail` | update | photos.thumbnail.update | +| DELETE | `/photos/{photo}/thumbnail` | destroy | photos.thumbnail.destroy | + +Если вы хотите, чтобы Laravel зарегистрировал маршрут `DELETE` для одиночного ресурса, но не регистрировал маршруты создания или сохранения, то вы можете использовать метод `destroyable`: + +```php +Route::singleton(...)->destroyable(); +``` + + +#### Одиночные ресурсы API-маршрутов + +Метод `apiSingleton` может использоваться для регистрации одиночного ресурса API, исключающим маршруты `create` и `edit`: + +```php +Route::apiSingleton('profile', ProfileController::class); +``` + +Конечно, одиночные ресурсы API также могут быть расширенными, содержать маршруты `store` и `destroy` ресурса: + +```php +Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable(); +``` ## Внедрение зависимостей и контроллеры diff --git a/docs/csrf.md b/docs/csrf.md index c6c81be..04167e0 100644 --- a/docs/csrf.md +++ b/docs/csrf.md @@ -94,7 +94,8 @@ Laravel автоматически генерирует «токен» CSRF дл ]; } -> {tip} Для удобства посредник CSRF автоматически отключается для всех маршрутов при [выполнение тестов](testing.md). +> **Примечание**\ +> Для удобства посредник CSRF автоматически отключается для всех маршрутов при [выполнение тестов](testing.md). ## Токен X-CSRF @@ -122,4 +123,5 @@ Laravel хранит текущий токен CSRF в зашифрованно Этот файл Cookies, в первую очередь, отправляется для удобства разработчика, поскольку некоторые фреймворки и библиотеки JavaScript, такие как Angular и Axios, автоматически помещают его значение в заголовок `X-XSRF-TOKEN` в запросах с одним и тем же источником. -> {tip} По умолчанию файл `resources/js/bootstrap.js` включает HTTP-библиотеку Axios, которая автоматически отправляет заголовок `X-XSRF-TOKEN`. +> **Примечание**\ +> По умолчанию файл `resources/js/bootstrap.js` включает HTTP-библиотеку Axios, которая автоматически отправляет заголовок `X-XSRF-TOKEN`. diff --git a/docs/database-testing.md b/docs/database-testing.md index fb4c3c0..3281cbf 100644 --- a/docs/database-testing.md +++ b/docs/database-testing.md @@ -2,21 +2,7 @@ - [Введение](#introduction) - [Сброс базы данных после каждого теста](#resetting-the-database-after-each-test) -- [Определение фабрик моделей](#defining-model-factories) - - [Обзор концепции](#concept-overview) - - [Генерация фабрик](#generating-factories) - - [Состояния фабрик](#factory-states) - - [Хуки фабрик](#factory-callbacks) -- [Создание моделей с использованием фабрик](#creating-models-using-factories) - - [Инициализация экземпляров моделей](#instantiating-models) - - [Сохранение моделей](#persisting-models) - - [Последовательность состояний](#sequences) -- [Отношения](#factory-relationships) - - [Отношения Has Many](#has-many-relationships) - - [Отношения Belongs To](#belongs-to-relationships) - - [Отношения Many To Many](#many-to-many-relationships) - - [Полиморфные отношения](#polymorphic-relationships) - - [Определение отношений внутри фабрик](#defining-relationships-within-factories) +- [Фабрики моделей](#model-factories) - [Запуск наполнителей](#running-seeders) - [Доступные утверждения](#available-assertions) @@ -59,484 +45,20 @@ Laravel предлагает множество полезных инструм Если вы хотите полностью сбросить базу данных с помощью миграции, то вы можете вместо этого использовать трейт `Illuminate\Foundation\Testing\DatabaseMigrations`. Однако использование трейта `DatabaseMigrations` значительно медленнее, чем использование трейта `RefreshDatabase`. - -## Определение фабрик моделей + +## Фабрики моделей - -### Обзор концепции +При тестировании вам может потребоваться вставить несколько записей в вашу базу данных перед выполнением теста. Вместо того, чтобы вручную указывать значение каждого столбца при создании этих тестовых данных, Laravel позволяет вам определять набор атрибутов по умолчанию для каждой из ваших [моделей Eloquent](eloquent.md), используя [фабрики моделей](eloquent-factories.md). -Сначала поговорим о фабриках моделей Eloquent. При тестировании вам может потребоваться вставить несколько записей в вашу базу данных перед выполнением теста. Вместо того, чтобы вручную указывать значение каждого столбца при создании этих тестовых данных, Laravel позволяет вам определять набор атрибутов по умолчанию для каждой из ваших [моделей Eloquent](eloquent.md), используя фабрики моделей. - -Чтобы увидеть пример написания фабрики, взгляните на файл `database/factories/UserFactory.php` в вашем приложении. Эта фабрика включена во все новые приложения Laravel и содержит следующее определение фабрики: - - namespace Database\Factories; - - use Illuminate\Database\Eloquent\Factories\Factory; - use Illuminate\Support\Str; - - class UserFactory extends Factory - { - /** - * Определить состояние модели по умолчанию. - * - * @return array - */ - public function definition() - { - return [ - 'name' => $this->faker->name(), - 'email' => $this->faker->unique()->safeEmail(), - 'email_verified_at' => now(), - 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password - 'remember_token' => Str::random(10), - ]; - } - } - -Как видите, фабрики – это классы, которые расширяют базовый класс фабрики Laravel и определяют метод `definition`. Метод `definition` возвращает набор значений атрибутов по умолчанию, которые должны применяться при создании модели с использованием фабрики. - -Через свойство `$faker` фабрики получают доступ к библиотеке [Faker](https://github.com/FakerPHP/Faker) PHP, которая позволяет вам удобно генерировать различные виды случайных данных для тестирования. - -> {tip} Вы можете установить языковой стандарт Faker для своего приложения, добавив опцию `faker_locale` в конфигурационном файле `config/app.php`. - - -### Генерация фабрик - -Чтобы сгенерировать новую фабрику, используйте команду `make:factory` [Artisan](artisan.md): - -```shell -php artisan make:factory PostFactory -``` - -Эта команда поместит новый класс фабрики в каталог `database/factories` вашего приложения. - - -#### Соглашения обнаружения фабрики и модели - -После того определения фабрик, вы можете использовать статический метод `factory` трейта `Illuminate\Database\Eloquent\Factories\HasFactory` для создания экземпляра фабрики текущей модели. - -Метод `factory` трейта `HasFactory` будет использовать соглашения для определения подходящей фабрики для модели, которой назначен трейт. В частности, метод будет искать фабрику в пространстве имен `Database\Factories`, имя класса которой соответствует имени модели и имеет суффикс `Factory`. Если эти соглашения не применимы к вашему конкретному приложению или фабрике, то вы можете переопределить метод `newFactory` вашей модели, чтобы напрямую вернуть экземпляр соответствующей фабрики модели: - - use Database\Factories\Administration\FlightFactory; - - /** - * Создать новый экземпляр фабрики для модели. - * - * @return \Illuminate\Database\Eloquent\Factories\Factory - */ - protected static function newFactory() - { - return FlightFactory::new(); - } - -Затем определите свойство `model` соответствующей фабрики: - - use App\Administration\Flight; - use Illuminate\Database\Eloquent\Factories\Factory; - - class FlightFactory extends Factory - { - /** - * Имя модели соответствующей фабрики. - * - * @var string - */ - protected $model = Flight::class; - } - - -### Состояния фабрик - -Методы управления состоянием позволяют вам определять дискретные изменения, которые могут быть применены к вашим фабрикам моделей в любой их комбинации. Например, ваша фабрика `Database\Factories\UserFactory` может содержать метод состояния `suspended`, который изменяет одно из значений атрибута по умолчанию. - -Методы преобразования состояния обычно вызывают метод `state` базового класса фабрики Laravel. Метод `state` принимает замыкание, которое получит массив изначально определенных для фабрики атрибутов, и должен вернуть массив изменяемых атрибутов: - - /** - * Указать, что аккаунт пользователя временно приостановлен. - * - * @return \Illuminate\Database\Eloquent\Factories\Factory - */ - public function suspended() - { - return $this->state(function (array $attributes) { - return [ - 'account_status' => 'suspended', - ]; - }); - } - -#### Состояние `trashed` (временно удаленных) - -Если ваша модель Eloquent поддерживает [программное удаление](eloquent.md#soft-deleting), то вы можете вызвать метод состояния `trashed` для указания, что созданная модель уже должна быть «программно удаленной». Вам не нужно самостоятельно определять состояние `trashed`, так как оно автоматически доступно для всех фабрик: - - use App\Models\User; - - $user = User::factory()->trashed()->create(); - - -### Хуки фабрик - -Хуки фабрик регистрируются с использованием методов `afterMaking` и `afterCreating` и позволяют выполнять дополнительные задачи после инициализации или создания модели. Вы должны зарегистрировать эти хуки, переопределив метод `configure` в вашем классе фабрики. Этот метод будет автоматически вызываться Laravel при создании экземпляра фабрики: - - namespace Database\Factories; - - use App\Models\User; - use Illuminate\Database\Eloquent\Factories\Factory; - use Illuminate\Support\Str; - - class UserFactory extends Factory - { - /** - * Конфигурация фабрики модели. - * - * @return $this - */ - public function configure() - { - return $this->afterMaking(function (User $user) { - // - })->afterCreating(function (User $user) { - // - }); - } - - // ... - } - - -## Создание моделей с использованием фабрик - - -### Инициализация экземпляров моделей - -После того, как вы определили свои фабрики, вы можете использовать статический метод `factory`, предоставляемый вашим моделям с помощью трейта `Illuminate\Database\Eloquent\Factories\HasFactory`, чтобы инициализировать экземпляр фабрики для этой модели. Давайте посмотрим на несколько примеров создания моделей. Во-первых, мы воспользуемся методом `make` для создания моделей, но без их сохранения в базе данных: +Чтобы узнать больше о создании и использовании фабрик моделей, обратитесь к полной [документации фабрики моделей](eloquent-factories.md). После того, как вы определили фабрику модели, вы можете использовать фабрику в своем тесте для создания моделей: use App\Models\User; public function test_models_can_be_instantiated() { - $user = User::factory()->make(); - - // Использование модели в тестах ... - } - -Вы можете создать коллекцию из множества моделей, используя метод `count`: - - $users = User::factory()->count(3)->make(); - - -#### Применение состояний - -Вы также можете применить к моделям любое из ваших [состояний](#factory-states). Если вы хотите применить к моделям несколько изменений состояния, то вы можете просто вызвать методы преобразования состояния напрямую: - - $users = User::factory()->count(5)->suspended()->make(); - - -#### Переопределение атрибутов - -Если вы хотите переопределить некоторые значения по умолчанию для ваших моделей, вы можете передать массив значений методу `make`. Будут заменены только указанные атрибуты, в то время как для остальных атрибутов сохранятся значения по умолчанию, указанные в фабрике: - - $user = User::factory()->make([ - 'name' => 'Abigail Otwell', - ]); - -В качестве альтернативы, метод `state` может быть вызван непосредственно на экземпляре фабрики для выполнения быстрого преобразования состояния: - - $user = User::factory()->state([ - 'name' => 'Abigail Otwell', - ])->make(); - -> {tip} [Защита от массового присвоения](eloquent.md#mass-assignment) автоматически отключается при создании моделей с использованием фабрик. - - -### Сохранение моделей - -Метод `create` инициализирует экземпляры модели и сохраняет их в базе данных с помощью метода `save` модели Eloquent: - - use App\Models\User; - - public function test_models_can_be_persisted() - { - // Создаем один экземпляр `App\Models\User` ... $user = User::factory()->create(); - // Создаем три экземпляра `App\Models\User` ... - $users = User::factory()->count(3)->create(); - - // Использование модели в тестах ... - } - -Вы можете переопределить атрибуты модели по умолчанию, передав массив атрибутов методу `create`: - - $user = User::factory()->create([ - 'name' => 'Abigail', - ]); - - -### Последовательность состояний - -По желанию можно изменить значение конкретного атрибута модели для каждой вновь созданной модели. Вы можете добиться этого, определив преобразование состояния как последовательность. Например, вы можете изменять значение столбца `admin` между `Y` и `N` для каждого вновь созданного пользователя: - - use App\Models\User; - use Illuminate\Database\Eloquent\Factories\Sequence; - - $users = User::factory() - ->count(10) - ->state(new Sequence( - ['admin' => 'Y'], - ['admin' => 'N'], - )) - ->create(); - -В этом примере пять пользователей будут созданы со значением `admin`, равным `Y`, и пять пользователей – со значением `admin`, равным `N`. - -При необходимости вы можете внедрить замыкание в качестве значения последовательности. Замыкание будет вызываться каждый раз, когда последовательности потребуется новое значение: - - $users = User::factory() - ->count(10) - ->state(new Sequence( - fn ($sequence) => ['role' => UserRoles::all()->random()], - )) - ->create(); - -Внутри замыкания последовательности вы можете получить доступ к свойствам `$index` или `$count` экземпляра последовательности, переданной в замыкание. Свойство `$index` содержит количество выполненных итераций последовательности, а свойство `$count` содержит общее количество вызываемых итераций раз последовательности: - - $users = User::factory() - ->count(10) - ->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index]) - ->create(); - - -## Отношения - - -### Отношения Has Many - -Затем, давайте рассмотрим построение отношений модели Eloquent с использованием текучего интерфейса методов фабрик Laravel. Во-первых, предположим, что у нашего приложения есть модель `App\Models\User` и модель `App\Models\Post`. Также предположим, что модель `User` определяет отношения `hasMany` с `Post`. Мы можем создать пользователя с тремя постами, используя метод `has`, предоставляемый фабриками Laravel. Метод `has` принимает экземпляр фабрики: - - use App\Models\Post; - use App\Models\User; - - $user = User::factory() - ->has(Post::factory()->count(3)) - ->create(); - -По соглашению, при передаче модели `Post` методу `has`, Laravel будет предполагать, что модель `User` должна иметь метод `posts`, который определяет отношения. При необходимости вы можете явно указать имя отношения, которым вы хотите управлять: - - $user = User::factory() - ->has(Post::factory()->count(3), 'posts') - ->create(); - -Конечно, вы можете выполнять манипуляции с состоянием связанных моделей. Кроме того, вы можете передать преобразование состояния на основе замыкания, если для изменения вашего состояния требуется доступ к родительской модели: - - $user = User::factory() - ->has( - Post::factory() - ->count(3) - ->state(function (array $attributes, User $user) { - return ['user_type' => $user->type]; - }) - ) - ->create(); - - -#### Использование магических методов Has Many - -Для удобства вы можете использовать магические методы отношений фабрики Laravel для построения отношений. Например, в следующем примере будет использоваться соглашение, чтобы определить, что связанные модели должны быть созданы с помощью метода отношений `posts` модели `User`: - - $user = User::factory() - ->hasPosts(3) - ->create(); - -При использовании магических методов для создания отношений фабрики вы можете передать массив атрибутов для их переопределения в связанных моделях: - - $user = User::factory() - ->hasPosts(3, [ - 'published' => false, - ]) - ->create(); - -Вы можете предоставить преобразование состояния на основе замыкания, если для изменения состояния требуется доступ к родительской модели: - - $user = User::factory() - ->hasPosts(3, function (array $attributes, User $user) { - return ['user_type' => $user->type]; - }) - ->create(); - - -### Отношения Belongs To - -Теперь, когда мы изучили, как построить отношения Has Many с помощью фабрик, давайте рассмотрим обратное отношение. Метод `for` используется для определения родительской модели, к которой принадлежат модели, созданные фабрикой. Например, мы можем создать три экземпляра модели `App\Models\Post`, которые принадлежат одному пользователю: - - use App\Models\Post; - use App\Models\User; - - $posts = Post::factory() - ->count(3) - ->for(User::factory()->state([ - 'name' => 'Jessica Archer', - ])) - ->create(); - -Если у вас уже есть экземпляр родительской модели, который должен быть связан с моделями, которые вы создаете, вы можете передать экземпляр модели методу `for`: - - $user = User::factory()->create(); - - $posts = Post::factory() - ->count(3) - ->for($user) - ->create(); - - -#### Использование магических методов Belongs To - -Для удобства вы можете использовать магические методы отношений фабрики Laravel для построения отношений Belongs To. Например, в следующем примере будет использоваться соглашение, чтобы определить, что три поста должны принадлежать отношениям `user` в модели `Post`: - - $posts = Post::factory() - ->count(3) - ->forUser([ - 'name' => 'Jessica Archer', - ]) - ->create(); - - -### Отношения Many To Many - -Как и [отношения Has Many](#has-many-relationships), отношения Many To Many могут быть созданы с использованием метода `has`: - - use App\Models\Role; - use App\Models\User; - - $user = User::factory() - ->has(Role::factory()->count(3)) - ->create(); - - -#### Атрибуты сводной таблицы - -Если вам нужно определить атрибуты, которые должны быть установлены в сводной / промежуточной таблице, связывающей модели, вы можете использовать метод `hasAttached`. Этот метод принимает в качестве второго аргумента массив имен и значений атрибутов сводной таблицы: - - use App\Models\Role; - use App\Models\User; - - $user = User::factory() - ->hasAttached( - Role::factory()->count(3), - ['active' => true] - ) - ->create(); - -Вы можете предоставить преобразование состояния на основе замыкания, если для изменения состояния требуется доступ к связанной модели: - - $user = User::factory() - ->hasAttached( - Role::factory() - ->count(3) - ->state(function (array $attributes, User $user) { - return ['name' => $user->name.' Role']; - }), - ['active' => true] - ) - ->create(); - -Если у вас уже есть экземпляры модели, которые вы хотите прикрепить к создаваемым моделям, вы можете передать экземпляры модели методу `hasAttached`. В этом примере всем трем пользователям будут назначены одни и те же три роли: - - $roles = Role::factory()->count(3)->create(); - - $user = User::factory() - ->count(3) - ->hasAttached($roles, ['active' => true]) - ->create(); - - -#### Использование магических методов Many To Many - -Для удобства вы можете использовать магические методы отношений фабрики Laravel для построения отношений Many To Many. Например, в следующем примере будет использоваться соглашение, чтобы определить, что связанные модели должны быть созданы с помощью метода отношений `roles` модели `User`: - - $user = User::factory() - ->hasRoles(1, [ - 'name' => 'Editor' - ]) - ->create(); - - -### Полиморфные отношения - -[Полиморфные отношения](eloquent-relationships.md#polymorphic-relationships) также могут быть созданы с использованием фабрик. Полиморфные отношения Morph Many создаются так же, как типичные отношения Has Many. Например, если модель `App\Models\Post` имеет отношение `morphMany` с моделью `App\Models\Comment`: - - use App\Models\Post; - - $post = Post::factory()->hasComments(3)->create(); - - -#### Отношения Morph To - -Магические методы нельзя использовать для создания отношений `morphTo`. Вместо этого метод `for` должен использоваться напрямую, а имя отношения должно быть явно указано. Например, представьте, что модель `Comment` имеет метод `commentable`, который определяет отношение `morphTo`. В этой ситуации мы можем создать три комментария, относящиеся к одному посту, используя напрямую метод `for`: - - $comments = Comment::factory()->count(3)->for( - Post::factory(), 'commentable' - )->create(); - - -#### Полиморфные отношения Many To Many - -Полиморфные отношения Many To Many (`morphToMany` / `morphedByMany`) могут быть созданы точно так же, как неполиморфные отношения Many To Many: - - use App\Models\Tag; - use App\Models\Video; - - $videos = Video::factory() - ->hasAttached( - Tag::factory()->count(3), - ['public' => true] - ) - ->create(); - -Конечно, магический метод `has` также используется для создания полиморфных отношений Many To Many: - - $videos = Video::factory() - ->hasTags(3, ['public' => true]) - ->create(); - - -### Определение отношений внутри фабрик - -Чтобы определить отношение в рамках вашей фабрики модели, вы обычно назначаете новый экземпляр фабрики внешнему ключу отношения. Обычно это делается для «обратных» отношений, таких как `belongsTo` и `morphTo`. Например, если вы хотите создать нового пользователя при создании публикации, вы можете сделать следующее: - - use App\Models\User; - - /** - * Определить состояние модели по умолчанию. - * - * @return array - */ - public function definition() - { - return [ - 'user_id' => User::factory(), - 'title' => $this->faker->title(), - 'content' => $this->faker->paragraph(), - ]; - } - -Если столбцы отношения зависят от фабрики, которая его определяет, вы можете назначить замыкание атрибуту. Замыкание получит массив проанализированных атрибутов фабрики: - - /** - * Определить состояние модели по умолчанию. - * - * @return array - */ - public function definition() - { - return [ - 'user_id' => User::factory(), - 'user_type' => function (array $attributes) { - return User::find($attributes['user_id'])->type; - }, - 'title' => $this->faker->title(), - 'content' => $this->faker->paragraph(), - ]; + // ... } @@ -650,6 +172,13 @@ Laravel содержит несколько утверждений базы да $this->assertSoftDeleted($user); + +#### assertNotSoftDeleted + +Метод `assertSoftDeleted` используется для утверждения того, что переданная модель Eloquent не была «программно удалена»: + + $this->assertNotSoftDeleted($user); + #### assertModelExists diff --git a/docs/database.md b/docs/database.md index aea940f..3b4c627 100644 --- a/docs/database.md +++ b/docs/database.md @@ -6,8 +6,11 @@ - [Выполнение SQL-запросов](#running-queries) - [Использование нескольких соединений к базе данных](#using-multiple-database-connections) - [Прослушивание событий запроса](#listening-for-query-events) + - [Мониторинг совокупного времени запроса](#monitoring-cumulative-query-time) - [Транзакции базы данных](#database-transactions) - [Подключение к базе данных с помощью интерфейса командной строки](#connecting-to-the-database-cli) +- [Просмотр сведений ваших баз данных](#inspecting-your-databases) +- [Мониторинг ваших баз данных](#monitoring-your-databases) ## Введение @@ -16,7 +19,7 @@ -- MariaDB 10.2+ ([Политика версий](https://mariadb.org/about/#maintenance-policy)) +- MariaDB 10.3+ ([Политика версий](https://mariadb.org/about/#maintenance-policy)) - MySQL 5.7+ ([Политика версий](https://en.wikipedia.org/wiki/MySQL#Release_history)) - PostgreSQL 10.0+ ([Политика версий](https://www.postgresql.org/support/versioning/)) - SQLite 3.8.8+ @@ -213,7 +216,8 @@ driver://username:password@host:port/database?options DB::unprepared('update users set votes = 100 where name = "Dries"'); -> {note} Поскольку неподготовленные запросы не связывают параметры, они могут быть уязвимы для SQL-инъекций. Вы никогда не должны пропускать в неподготовленное выражение значения, управляемые пользователем. +> **Предупреждение**\ +> Поскольку неподготовленные запросы не связывают параметры, они могут быть уязвимы для SQL-инъекций. Вы никогда не должны пропускать в неподготовленное выражение значения, управляемые пользователем. #### Неявные фиксации @@ -276,6 +280,45 @@ driver://username:password@host:port/database?options } } + +### Мониторинг совокупного времени запроса + +Общим узким местом производительности современных веб-приложений является количество времени, которое они тратят на запросы к базам данных. К счастью, Laravel может вызвать замыкание, когда тратится слишком много времени на запросы к базе данных в рамках одного запроса к приложению. Для начала укажите пороговое значение времени запроса (в миллисекундах) и замыкание для метода `whenQueryingForLongerThan`. Как правило, вызов этого метода осуществляется в методе `boot` [поставщика служб](providers.md): + + ## Транзакции базы данных @@ -319,7 +362,8 @@ driver://username:password@host:port/database?options DB::commit(); -> {tip} Методы транзакций фасада `DB` контролируют транзакции как для [построителя запросов](queries.md), так и для [Eloquent ORM](eloquent.md). +> **Примечание**\ +> Методы транзакций фасада `DB` контролируют транзакции как для [построителя запросов](queries.md), так и для [Eloquent ORM](eloquent.md). ## Подключение к базе данных с помощью интерфейса командной строки @@ -335,3 +379,69 @@ php artisan db ```shell php artisan db mysql ``` + + +## Просмотр сведений ваших баз данных + +Используя команды `db:show` и `db:table` Artisan, вы можете получить ценную информацию о своей базе данных и связанных с ней таблицах. Чтобы просмотреть свойства вашей базы данных, включая ее размер, тип, количество открытых подключений и сводку по таблицам, вы можете использовать команду `db:show`: + +```shell +php artisan db:show +``` + +Вы можете указать, какое соединение с базой данных следует просмотреть, указав имя соединения с базой данных с помощью параметра `--database`: + +```shell +php artisan db:show --database=pgsql +``` + +Если вы хотите включить количество строк в таблице и сведения о представлении базы данных в вывод команды, то вы можете указать параметры `--counts` и `--views` соответственно. В больших базах данных получение количества строк и сведений о представлениях может быть медленным: + +```shell +php artisan db:show --counts --views +``` + + +#### Table Overview + +Если вы хотите получить обзор отдельной таблицы в вашей базе данных, то вы можете выполнить команду `db:table` Artisan. Эта команда предоставляет общий обзор таблицы базы данных, включая ее столбцы, типы, атрибуты, ключи и индексы: + +```shell +php artisan db:table users +``` + + +## Мониторинг ваших баз данных + +Используя команду `db:monitor` Artisan, вы можете указать Laravel инициировать событие `Illuminate\Database\Events\DatabaseBusy`, если ваша база данных управляет более чем указанным количеством открытых соединений. + +Для начала вы должны запланировать команду `db:monitor` так, чтобы она [запускалась каждую минуту](scheduling.md). Команда принимает имена конфигураций подключения к базе данных, которые вы хотите отслеживать, а также максимальное количество открытых подключений, которое должно быть разрешено перед отправкой события: + +```shell +php artisan db:monitor --databases=mysql,pgsql --max=100 +``` + +Одного планирования этой команды недостаточно, чтобы отправить уведомление, предупреждающее вас о количестве открытых подключений. Когда команда обнаруживает базу данных, количество открытых подключений которой превышает пороговое значение, тогда будет инициировано событие `DatabaseBusy`. Вы должны прослушивать это событие в `EventServiceProvider` вашего приложения, чтобы отправить уведомление вам или вашей команде разработчиков: + +```php +use App\Notifications\DatabaseApproachingMaxConnections; +use Illuminate\Database\Events\DatabaseBusy; +use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\Notification; + +/** + * Регистрация любых событий вашего приложения. + * + * @return void + */ +public function boot() +{ + Event::listen(function (DatabaseBusy $event) { + Notification::route('mail', 'dev@example.com') + ->notify(new DatabaseApproachingMaxConnections( + $event->connectionName, + $event->connections + )); + }); +} +``` diff --git a/docs/deployment.md b/docs/deployment.md index 47bc6c2..bcfa995 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -97,7 +97,8 @@ server { composer install --optimize-autoloader --no-dev ``` -> {tip} Помимо оптимизации автозагрузчика, вы всегда должны обязательно включать файл `composer.lock` в репозиторий системы управления версиями вашего проекта. Зависимости вашего проекта могут быть установлены намного быстрее, если присутствует файл `composer.lock`. +> **Примечание**\ +> Помимо оптимизации автозагрузчика, вы всегда должны обязательно включать файл `composer.lock` в репозиторий системы управления версиями вашего проекта. Зависимости вашего проекта могут быть установлены намного быстрее, если присутствует файл `composer.lock`. ### Оптимизация загрузки конфигурации @@ -110,7 +111,8 @@ php artisan config:cache Эта команда объединит все файлы конфигурации Laravel в один кешированный файл, что значительно сократит количество обращений, которые фреймворк должен совершить к файловой системе при загрузке значений вашей конфигурации. -> {note} Если вы выполняете команду `config:cache` в процессе развертывания, вы должны быть уверены, что вызываете функцию `env` только из ваших файлов конфигурации. После кеширования конфигурации файл `.env` не будет загружаться, и все вызовы функции `env` для переменных файла `.env` вернут `null`. +> **Предупреждение**\ +> Если вы выполняете команду `config:cache` в процессе развертывания, вы должны быть уверены, что вызываете функцию `env` только из ваших файлов конфигурации. После кеширования конфигурации файл `.env` не будет загружаться, и все вызовы функции `env` для переменных файла `.env` вернут `null`. ### Оптимизация загрузки маршрута @@ -151,6 +153,9 @@ php artisan view:cache Laravel Forge может создавать серверы на различных поставщиках инфраструктуры, таких как DigitalOcean, Linode, AWS и других. Кроме того, Forge устанавливает и управляет всеми инструментами, необходимыми для создания надежных приложений Laravel, таких как Nginx, MySQL, Redis, Memcached, Beanstalk и других. +> **Примечание**\ +> Хотите полное руководство по развертыванию с помощью Laravel Forge? Посетите [Laravel Bootcamp](https://bootcamp.laravel.com/deploying) и серии видео о Forge, [доступные на Laracasts](https://laracasts.com/series/learn-laravel-forge-2022-edition). + #### Laravel Vapor diff --git a/docs/documentation.md b/docs/documentation.md index d70859e..54cdd55 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -6,6 +6,7 @@ - [Installation](/docs/{{version}}/installation) - [Configuration](/docs/{{version}}/configuration) - [Directory Structure](/docs/{{version}}/structure) + - [Frontend](/docs/{{version}}/frontend) - [Starter Kits](/docs/{{version}}/starter-kits) - [Deployment](/docs/{{version}}/deployment) - ## Architecture Concepts @@ -22,6 +23,7 @@ - [Responses](/docs/{{version}}/responses) - [Views](/docs/{{version}}/views) - [Blade Templates](/docs/{{version}}/blade) + - [Asset Bundling](/docs/{{version}}/vite) - [URL Generation](/docs/{{version}}/urls) - [Session](/docs/{{version}}/session) - [Validation](/docs/{{version}}/validation) @@ -32,7 +34,6 @@ - [Broadcasting](/docs/{{version}}/broadcasting) - [Cache](/docs/{{version}}/cache) - [Collections](/docs/{{version}}/collections) - - [Compiling Assets](/docs/{{version}}/mix) - [Contracts](/docs/{{version}}/contracts) - [Events](/docs/{{version}}/events) - [File Storage](/docs/{{version}}/filesystem) @@ -43,6 +44,7 @@ - [Notifications](/docs/{{version}}/notifications) - [Package Development](/docs/{{version}}/packages) - [Queues](/docs/{{version}}/queues) + - [Rate Limiting](/docs/{{version}}/rate-limiting) - [Task Scheduling](/docs/{{version}}/scheduling) - ## Security - [Authentication](/docs/{{version}}/authentication) @@ -65,6 +67,7 @@ - [Mutators / Casts](/docs/{{version}}/eloquent-mutators) - [API Resources](/docs/{{version}}/eloquent-resources) - [Serialization](/docs/{{version}}/eloquent-serialization) + - [Factories](/docs/{{version}}/eloquent-factories) - ## Testing - [Getting Started](/docs/{{version}}/testing) - [HTTP Tests](/docs/{{version}}/http-tests) @@ -82,7 +85,10 @@ - [Homestead](/docs/{{version}}/homestead) - [Horizon](/docs/{{version}}/horizon) - [Jetstream](https://jetstream.laravel.com) + - [Mix](/docs/{{version}}/mix) + - [Octane](/docs/{{version}}/octane) - [Passport](/docs/{{version}}/passport) + - [Pint](/docs/{{version}}/pint) - [Sail](/docs/{{version}}/sail) - [Sanctum](/docs/{{version}}/sanctum) - [Scout](/docs/{{version}}/scout) diff --git a/docs/dusk.md b/docs/dusk.md index 30f30a1..cbb0215 100644 --- a/docs/dusk.md +++ b/docs/dusk.md @@ -62,9 +62,10 @@ composer require --dev laravel/dusk ``` -> {note} Если вы вручную регистрируете поставщика `DuskServiceProvider`, то вам **никогда** не следует регистрировать его в рабочем окружении, так как это может привести к тому, что случайные пользователи смогут пройти аутентификацию в вашем приложении. +> **Предупреждение**\ +> Если вы вручную регистрируете поставщика `DuskServiceProvider`, то вам **никогда** не следует регистрировать его в рабочем окружении, так как это может привести к тому, что случайные пользователи смогут пройти аутентификацию в вашем приложении. -После установки пакета Dusk выполните команду `dusk:install` Artisan. Команда `dusk:install` создаст каталог `tests/Browser` и пример теста Dusk: +После установки пакета Dusk выполните команду `dusk:install` Artisan. Команда `dusk:install` создаст каталог `tests/Browser`, пример теста Dusk и установит бинарный файл драйвера Chrome для вашей операционной системы: ```shell php artisan dusk:install @@ -72,12 +73,13 @@ php artisan dusk:install Затем установите переменную окружения `APP_URL` в файле `.env` вашего приложения. Это значение должно соответствовать URL-адресу, который вы используете для доступа к вашему приложению в браузере. -> {tip} Если вы используете [Laravel Sail](sail.md) для управления своей локальной средой разработки, то обратитесь также к документации Sail по [настройке и запуску тестов Dusk](sail.md#laravel-dusk). +> **Примечание**\ +> Если вы используете [Laravel Sail](sail.md) для управления своей локальной средой разработки, то обратитесь также к документации Sail по [настройке и запуску тестов Dusk](sail.md#laravel-dusk). ### Управление установками ChromeDriver -Если вы хотите установить версию ChromeDriver, отличную от той, которая включена в Laravel Dusk, то вы можете использовать команду `dusk:chrome-driver`: +Если вы хотите установить версию ChromeDriver, отличную от версии, установленной Laravel Dusk с помощью команды `dusk:install`, то вы можете использовать команду `dusk:chrome-driver`: ```shell # Установить последнюю версию ChromeDriver для вашей ОС ... @@ -93,7 +95,8 @@ php artisan dusk:chrome-driver --all php artisan dusk:chrome-driver --detect ``` -> {note} Dusk требует, чтобы файлы `chromedriver` были доступны для выполнения. Если у вас возникли проблемы с запуском Dusk, то вы должны убедиться, что файлы доступны для выполнения, используя следующую команду: `chmod -R 0755 vendor/laravel/dusk/bin/`. +> **Предупреждение**\ +> Dusk требует, чтобы файлы `chromedriver` были доступны для выполнения. Если у вас возникли проблемы с запуском Dusk, то вы должны убедиться, что файлы доступны для выполнения, используя следующую команду: `chmod -R 0755 vendor/laravel/dusk/bin/`. ### Использование других браузеров @@ -158,7 +161,8 @@ php artisan dusk:make LoginTest use DatabaseMigrations; } -> {note} Базы данных SQLite, хранимые в памяти, нельзя использовать при выполнении тестов Dusk. Поскольку браузер выполняет свой собственный процесс, он не сможет получить доступ к базам данных, хранимых в памяти, других процессов. +> **Предупреждение**\ +> Базы данных SQLite, хранимые в памяти, нельзя использовать при выполнении тестов Dusk. Поскольку браузер выполняет свой собственный процесс, он не сможет получить доступ к базам данных, хранимых в памяти, других процессов. ### Запуск тестов @@ -175,13 +179,14 @@ php artisan dusk php artisan dusk:fails ``` -Команда `dusk` принимает любой аргумент, который обычно принимается тестером PHPUnit, например, позволяет вам запускать тесты только для указанной [группы](https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.group): +Команда `dusk` принимает любой аргумент, который обычно принимается тестером PHPUnit, например, позволяет вам запускать тесты только для указанной [группы](https://phpunit.readthedocs.io/en/9.5/annotations.html#group): ```shell php artisan dusk --group=foo ``` -> {tip} Если вы используете [Laravel Sail](sail.md) для управления своей локальной средой разработки, обратитесь к документации Sail по [настройке и запуску тестов Dusk](sail.md#laravel-dusk). +> **Примечание**\ +> Если вы используете [Laravel Sail](sail.md) для управления своей локальной средой разработки, обратитесь к документации Sail по [настройке и запуску тестов Dusk](sail.md#laravel-dusk). #### Запуск ChromeDriver вручную @@ -377,7 +382,8 @@ php artisan dusk --group=foo ->visit('/home'); }); -> {note} После использования метода `loginAs` сессия пользователя будет поддерживаться для всех тестов, находящихся в файле. +> **Предупреждение**\ +> После использования метода `loginAs` сессия пользователя будет поддерживаться для всех тестов, находящихся в файле. ### Cookies @@ -419,6 +425,10 @@ php artisan dusk --group=foo $browser->screenshot('filename'); +Метод `responsiveScreenshots` можно использовать для создания серии снимков экрана: + + $browser->responsiveScreenshots('filename'); + ### Сохранение вывода консоли на диск @@ -563,7 +573,8 @@ Dusk содержит множество методов для взаимоде $browser->attach('photo', __DIR__.'/photos/mountains.png'); -> {note} Функционал прикрепления требует, чтобы на вашем сервере было установлено и включено расширение `Zip` PHP. +> **Предупреждение**\ +> Функционал прикрепления требует, чтобы на вашем сервере было установлено и включено расширение `Zip` PHP. ### Нажатие кнопок @@ -593,7 +604,8 @@ Dusk содержит множество методов для взаимоде // ... } -> {note} Эти методы взаимодействуют с библиотеками jQuery. Если jQuery недоступен на странице, то Dusk автоматически вставит его на страницу, чтобы он был доступен во время теста. +> **Предупреждение**\ +> Эти методы взаимодействуют с библиотеками jQuery. Если jQuery недоступен на странице, то Dusk автоматически вставит его на страницу, чтобы он был доступен во время теста. ### Использование клавиатуры @@ -606,7 +618,8 @@ Dusk содержит множество методов для взаимоде $browser->keys('.app', ['{command}', 'j']); -> {tip} Все модификаторы клавиш, такие как `{command}` заключены в символы `{}` и соответствуют константам, определенным в классе `Facebook\WebDriver\WebDriverKeys`, который можно [найти на GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). +> **Примечание**\ +> Все модификаторы клавиш, такие как `{command}` заключены в символы `{}` и соответствуют константам, определенным в классе `Facebook\WebDriver\WebDriverKeys`, который можно [найти на GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). ### Использование мыши @@ -728,6 +741,14 @@ Dusk содержит различные методы для взаимодей $browser->pause(1000); +Если вам нужно приостановить тест только в том случае, если переданное условие истинно, то используйте метод `pauseIf`: + + $browser->pauseIf(App::environment('production'), 1000); + +Аналогичным образом, если вам нужно приостановить тест, если переданное условие не является истинным, то вы можете использовать метод `pauseUnless`: + + $browser->pauseUnless(App::environment('testing'), 1000); + #### Ожидание конкретных селекторов @@ -809,6 +830,17 @@ Dusk содержит различные методы для взаимодей // Ожидание видимости ссылки не более одной секунды ... $browser->waitForLink('Create', 1); + +#### Ожидание доступности полей ввода + +Метод `waitForInput` используется для ожидания появления поля ввода на странице: + + // Ожидание видимости поля ввода не более пяти секунд ... + $browser->waitForInput($field); + + // Ожидание видимости поля ввода не более одной секунды ... + $browser->waitForInput($field, 1); + #### Ожидание расположения страницы @@ -965,6 +997,7 @@ Dusk содержит множество утверждений, которые - [assertInputValueIsNot](#assert-input-value-is-not) - [assertChecked](#assert-checked) - [assertNotChecked](#assert-not-checked) +- [assertIndeterminate](#assert-indeterminate) - [assertRadioSelected](#assert-radio-selected) - [assertRadioNotSelected](#assert-radio-not-selected) - [assertSelected](#assert-selected) @@ -1280,6 +1313,13 @@ Dusk содержит множество утверждений, которые $browser->assertNotChecked($field); + +#### assertIndeterminate + +Утверждение о том, что переданный флажок находится в неопределенном состоянии: + + $browser->assertIndeterminate($field); + #### assertRadioSelected @@ -1817,7 +1857,8 @@ Dusk даже позволяет вам делать утверждения о ## Непрерывная интеграция -> {note} Большинство конфигураций непрерывной интеграции Dusk предполагают, что ваше приложение Laravel будет обслуживаться с помощью встроенного сервера разработки PHP на порту `8000`. Поэтому, прежде чем продолжить, вы должны убедиться, что ваша среда непрерывной интеграции имеет значение переменной окружения `APP_URL`, равное `http://127.0.0.1:8000`. +> **Предупреждение**\ +> Большинство конфигураций непрерывной интеграции Dusk предполагают, что ваше приложение Laravel будет обслуживаться с помощью встроенного сервера разработки PHP на порту `8000`. Поэтому, прежде чем продолжить, вы должны убедиться, что ваша среда непрерывной интеграции имеет значение переменной окружения `APP_URL`, равное `http://127.0.0.1:8000`. ### Heroku CI @@ -1870,7 +1911,7 @@ script: ### GitHub Actions -Если вы используете [Github Actions](https://github.com/features/actions) для запуска тестов Dusk, то вы можете использовать следующий конфигурационный файл в качестве отправной точки. Как и в случае с TravisCI, мы будем использовать команду `php artisan serve` для запуска встроенного веб-сервера PHP: +Если вы используете [GitHub Actions](https://github.com/features/actions) для запуска тестов Dusk, то вы можете использовать следующий конфигурационный файл в качестве отправной точки. Как и в случае с TravisCI, мы будем использовать команду `php artisan serve` для запуска встроенного веб-сервера PHP: ```yaml name: CI @@ -1879,27 +1920,30 @@ jobs: dusk-php: runs-on: ubuntu-latest + env: + APP_URL: "http://127.0.0.1:8000" + DB_USERNAME: root + DB_PASSWORD: root + MAIL_MAILER: log steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Prepare The Environment run: cp .env.example .env - name: Create Database run: | sudo systemctl start mysql - mysql --user="root" --password="root" -e "CREATE DATABASE 'my-database' character set UTF8mb4 collate utf8mb4_bin;" + mysql --user="root" --password="root" -e "CREATE DATABASE \`my-database\` character set UTF8mb4 collate utf8mb4_bin;" - name: Install Composer Dependencies run: composer install --no-progress --prefer-dist --optimize-autoloader - name: Generate Application Key run: php artisan key:generate - name: Upgrade Chrome Driver - run: php artisan dusk:chrome-driver `/opt/google/chrome/chrome --version | cut -d " " -f3 | cut -d "." -f1` + run: php artisan dusk:chrome-driver --detect - name: Start Chrome Driver run: ./vendor/laravel/dusk/bin/chromedriver-linux & - name: Run Laravel Server run: php artisan serve --no-reload & - name: Run Dusk Tests - env: - APP_URL: "http://127.0.0.1:8000" run: php artisan dusk - name: Upload Screenshots if: failure() diff --git a/docs/eloquent-factories.md b/docs/eloquent-factories.md new file mode 100644 index 0000000..b9cdd77 --- /dev/null +++ b/docs/eloquent-factories.md @@ -0,0 +1,509 @@ +# Laravel 9 · Eloquent · Фабрики + +- [Введение](#introduction) +- [Определение фабрик моделей](#defining-model-factories) + - [Генерация фабрик](#generating-factories) + - [Состояния фабрик](#factory-states) + - [Хуки фабрик](#factory-callbacks) +- [Создание моделей с использованием фабрик](#creating-models-using-factories) + - [Инициализация экземпляров моделей](#instantiating-models) + - [Сохранение моделей](#persisting-models) + - [Последовательность состояний](#sequences) +- [Отношения](#factory-relationships) + - [Отношения Has Many](#has-many-relationships) + - [Отношения Belongs To](#belongs-to-relationships) + - [Отношения Many To Many](#many-to-many-relationships) + - [Полиморфные отношения](#polymorphic-relationships) + - [Определение отношений внутри фабрик](#defining-relationships-within-factories) + - [Переиспользование существующей модели в отношениях](#recycling-an-existing-model-for-relationships) + + +## Введение + +При тестировании приложения или заполнении базы данных вам может потребоваться вставить несколько записей в базу данных. Вместо того, чтобы вручную указывать значение каждого столбца, Laravel позволяет вам определить набор атрибутов по умолчанию для каждой из ваших [моделей Eloquent](eloquent.md) + +Чтобы увидеть пример написания фабрики, взгляните на файл `database/factories/UserFactory.php` в вашем приложении. Эта фабрика включена во все новые приложения Laravel и содержит следующее определение фабрики: + + namespace Database\Factories; + + use Illuminate\Database\Eloquent\Factories\Factory; + use Illuminate\Support\Str; + + class UserFactory extends Factory + { + /** + * Определить состояние модели по умолчанию. + * + * @return array + */ + public function definition() + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'remember_token' => Str::random(10), + ]; + } + } + +Как видите, фабрики – это классы, которые расширяют базовый класс фабрики Laravel и определяют метод `definition`. Метод `definition` возвращает набор значений атрибутов по умолчанию, которые должны применяться при создании модели с использованием фабрики. + +Через помощника `fake` фабрики имеют доступ к библиотеке [Faker](https://github.com/FakerPHP/Faker) PHP, которая позволяет удобно генерировать различные виды случайных данных для тестирования и наполнения. + +> **Примечание**\ +> Вы можете установить языковой стандарт Faker для своего приложения, добавив опцию `faker_locale` в конфигурационном файле `config/app.php`. + + +## Определение фабрик моделей + + +### Генерация фабрик + +Чтобы сгенерировать фабрику, используйте команду `make:factory` [Artisan](artisan.md): + +```shell +php artisan make:factory PostFactory +``` + +Новый класс фабрики будет помещен в каталог `database/factories` вашего приложения. + + +#### Соглашения обнаружения фабрики и модели + +После того, как вы определили свои фабрики, вы можете использовать статический метод `factory` трейта `Illuminate\Database\Eloquent\Factories\HasFactory`, предоставленный вашим моделям, для создания экземпляра фабрики этой модели. + +Метод `factory` трейта `HasFactory` будет использовать соглашения для определения надлежащей фабрики для модели, которой назначен трейт. В частности, метод будет искать фабрику в пространстве имен `Database\Factories`, имя класса которой совпадает с именем модели и имеет суффикс `Factory`. Если эти соглашения не применяются к вашему конкретному приложению или фабрике, то вы можете перезаписать метод `newFactory` в вашей модели, чтобы напрямую возвращать экземпляр соответствующей фабрики: + + use Database\Factories\Administration\FlightFactory; + + /** + * Создать новый экземпляр фабрики для модели. + * + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + protected static function newFactory() + { + return FlightFactory::new(); + } + +Затем определите свойство `model` у соответствующей фабрики: + + use App\Administration\Flight; + use Illuminate\Database\Eloquent\Factories\Factory; + + class FlightFactory extends Factory + { + /** + * Имя модели соответствующей фабрики. + * + * @var string + */ + protected $model = Flight::class; + } + + +### Состояния фабрик + +Методы управления состоянием позволяют вам определять дискретные изменения, которые могут быть применены к вашим фабрикам моделей в любой их комбинации. Например, ваша фабрика `Database\Factories\UserFactory` может содержать метод состояния `suspended`, который изменяет одно из значений атрибута по умолчанию. + +Методы преобразования состояния обычно вызывают метод `state` базового класса фабрики Laravel. Метод `state` принимает замыкание, которое получит массив изначально определенных для фабрики атрибутов, и должен вернуть массив измененных атрибутов: + + /** + * Указать, что аккаунт пользователя временно приостановлен. + * + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + public function suspended() + { + return $this->state(function (array $attributes) { + return [ + 'account_status' => 'suspended', + ]; + }); + } + +#### Состояние временно удаленных + +Если ваша модель Eloquent поддерживает [программное удаление](eloquent.md#soft-deleting), то вы можете вызвать метод состояния `trashed` для указания, что созданная модель уже должна быть «программно удаленной». Вам не нужно самостоятельно определять состояние `trashed`, так как оно автоматически доступно для всех фабрик: + + use App\Models\User; + + $user = User::factory()->trashed()->create(); + + +### Хуки фабрик + +Хуки фабрик регистрируются с использованием методов `afterMaking` и `afterCreating` и позволяют выполнять дополнительные задачи после инициализации или создания модели. Вы должны зарегистрировать эти хуки, переопределив метод `configure` в вашем классе фабрики. Этот метод будет автоматически вызываться Laravel при создании экземпляра фабрики: + + namespace Database\Factories; + + use App\Models\User; + use Illuminate\Database\Eloquent\Factories\Factory; + use Illuminate\Support\Str; + + class UserFactory extends Factory + { + /** + * Конфигурация фабрики модели. + * + * @return $this + */ + public function configure() + { + return $this->afterMaking(function (User $user) { + // + })->afterCreating(function (User $user) { + // + }); + } + + // ... + } + + +## Создание моделей с использованием фабрик + + +### Инициализация экземпляров моделей + +После того, как вы определили свои фабрики, вы можете использовать статический метод `factory`, предоставленный вашим моделям с помощью трейта `Illuminate\Database\Eloquent\Factories\HasFactory`, чтобы инициализировать экземпляр фабрики для этой модели. Давайте посмотрим на несколько примеров создания моделей. Во-первых, мы воспользуемся методом `make` для создания моделей, но без их сохранения в базе данных: + + use App\Models\User; + + $user = User::factory()->make(); + +Вы можете создать коллекцию из множества моделей, используя метод `count`: + + $users = User::factory()->count(3)->make(); + + +#### Применение состояний + +Вы также можете применить к моделям любое из ваших [состояний](#factory-states). Если вы хотите применить к моделям несколько изменений состояния, то вы можете просто вызвать методы преобразования состояния напрямую: + + $users = User::factory()->count(5)->suspended()->make(); + + +#### Переопределение атрибутов + +Если вы хотите переопределить некоторые значения по умолчанию для ваших моделей, то вы можете передать массив значений методу `make`. Будут заменены только указанные атрибуты, в то время как для остальных атрибутов сохранятся значения по умолчанию, указанные в фабрике: + + $user = User::factory()->make([ + 'name' => 'Abigail Otwell', + ]); + +В качестве альтернативы, метод `state` может быть вызван непосредственно на экземпляре фабрики для выполнения быстрого преобразования состояния: + + $user = User::factory()->state([ + 'name' => 'Abigail Otwell', + ])->make(); + +> **Примечание**\ +> [Защита от массового присвоения](eloquent.md#mass-assignment) автоматически отключается при создании моделей с использованием фабрик. + + +### Сохранение моделей + +Метод `create` инициализирует экземпляры модели и сохраняет их в базе данных с помощью метода `save` модели Eloquent: + + use App\Models\User; + + // Создать один экземпляр `App\Models\User` ... + $user = User::factory()->create(); + + // Создать три экземпляра `App\Models\User` ... + $users = User::factory()->count(3)->create(); + +Вы можете переопределить атрибуты модели по умолчанию, передав массив атрибутов методу `create`: + + $user = User::factory()->create([ + 'name' => 'Abigail', + ]); + + +### Последовательность состояний + +По желанию можно изменить значение конкретного атрибута модели для каждой вновь созданной модели. Вы можете добиться этого, определив преобразование состояния как последовательность. Например, вы можете изменять значение столбца `admin` между `Y` и `N` для каждого вновь созданного пользователя: + + use App\Models\User; + use Illuminate\Database\Eloquent\Factories\Sequence; + + $users = User::factory() + ->count(10) + ->state(new Sequence( + ['admin' => 'Y'], + ['admin' => 'N'], + )) + ->create(); + +В этом примере пять пользователей будут созданы со значением `admin`, равным `Y`, и пять пользователей – со значением `admin`, равным `N`. + +При необходимости вы можете внедрить замыкание в качестве значения последовательности. Замыкание будет вызываться каждый раз, когда последовательности потребуется новое значение: + + $users = User::factory() + ->count(10) + ->state(new Sequence( + fn ($sequence) => ['role' => UserRoles::all()->random()], + )) + ->create(); + +Внутри замыкания последовательности вы можете получить доступ к свойствам `$index` или `$count` экземпляра последовательности, переданного в замыкание. Свойство `$index` содержит количество выполненных итераций последовательности, а свойство `$count` содержит общее количество вызываемых итераций последовательности: + + $users = User::factory() + ->count(10) + ->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index]) + ->create(); + + +## Отношения + + +### Отношения Has Many + +Затем, давайте рассмотрим построение отношений модели Eloquent с использованием текучего интерфейса методов фабрик Laravel. Во-первых, предположим, что у нашего приложения есть модель `App\Models\User` и модель `App\Models\Post`. Также предположим, что модель `User` определяет отношения `hasMany` с `Post`. Мы можем создать пользователя с тремя постами, используя метод `has`, предоставляемый фабриками Laravel. Метод `has` принимает экземпляр фабрики: + + use App\Models\Post; + use App\Models\User; + + $user = User::factory() + ->has(Post::factory()->count(3)) + ->create(); + +По соглашению, при передаче модели `Post` методу `has`, Laravel будет предполагать, что модель `User` должна иметь метод `posts`, который определяет отношения. При необходимости вы можете явно указать имя отношения, которым вы хотите управлять: + + $user = User::factory() + ->has(Post::factory()->count(3), 'posts') + ->create(); + +Конечно, вы можете выполнять манипуляции с состоянием связанных моделей. Кроме того, вы можете передать преобразование состояния на основе замыкания, если для изменения вашего состояния требуется доступ к родительской модели: + + $user = User::factory() + ->has( + Post::factory() + ->count(3) + ->state(function (array $attributes, User $user) { + return ['user_type' => $user->type]; + }) + ) + ->create(); + + +#### Использование магических методов Has Many + +Для удобства вы можете использовать магические методы отношений фабрики Laravel для построения отношений. Например, в следующем примере будет использоваться соглашение, чтобы определить, что связанные модели должны быть созданы с помощью метода отношений `posts` модели `User`: + + $user = User::factory() + ->hasPosts(3) + ->create(); + +При использовании магических методов для создания отношений фабрики вы можете передать массив атрибутов для их переопределения в связанных моделях: + + $user = User::factory() + ->hasPosts(3, [ + 'published' => false, + ]) + ->create(); + +Вы можете предоставить преобразование состояния на основе замыкания, если для изменения состояния требуется доступ к родительской модели: + + $user = User::factory() + ->hasPosts(3, function (array $attributes, User $user) { + return ['user_type' => $user->type]; + }) + ->create(); + + +### Отношения Belongs To + +Теперь, когда мы изучили, как построить отношения Has Many с помощью фабрик, давайте рассмотрим обратное отношение. Метод `for` используется для определения родительской модели, к которой принадлежат модели, созданные фабрикой. Например, мы можем создать три экземпляра модели `App\Models\Post`, которые принадлежат одному пользователю: + + use App\Models\Post; + use App\Models\User; + + $posts = Post::factory() + ->count(3) + ->for(User::factory()->state([ + 'name' => 'Jessica Archer', + ])) + ->create(); + +Если у вас уже есть экземпляр родительской модели, который должен быть связан с моделями, которые вы создаете, вы можете передать экземпляр модели методу `for`: + + $user = User::factory()->create(); + + $posts = Post::factory() + ->count(3) + ->for($user) + ->create(); + + +#### Использование магических методов Belongs To + +Для удобства вы можете использовать магические методы отношений фабрики Laravel для построения отношений Belongs To. Например, в следующем примере будет использоваться соглашение, чтобы определить, что три поста должны принадлежать отношениям `user` в модели `Post`: + + $posts = Post::factory() + ->count(3) + ->forUser([ + 'name' => 'Jessica Archer', + ]) + ->create(); + + +### Отношения Many To Many + +Как и [отношения Has Many](#has-many-relationships), отношения Many To Many могут быть созданы с использованием метода `has`: + + use App\Models\Role; + use App\Models\User; + + $user = User::factory() + ->has(Role::factory()->count(3)) + ->create(); + + +#### Атрибуты сводной таблицы + +Если вам нужно определить атрибуты, которые должны быть установлены в сводной / промежуточной таблице, связывающей модели, вы можете использовать метод `hasAttached`. Этот метод принимает в качестве второго аргумента массив имен и значений атрибутов сводной таблицы: + + use App\Models\Role; + use App\Models\User; + + $user = User::factory() + ->hasAttached( + Role::factory()->count(3), + ['active' => true] + ) + ->create(); + +Вы можете передать замыкание для преобразования состояния, если для изменения состояния требуется доступ к связанной модели: + + $user = User::factory() + ->hasAttached( + Role::factory() + ->count(3) + ->state(function (array $attributes, User $user) { + return ['name' => $user->name.' Role']; + }), + ['active' => true] + ) + ->create(); + +Если у вас уже есть экземпляры модели, которые вы хотите прикрепить к создаваемым моделям, вы можете передать экземпляры модели методу `hasAttached`. В этом примере всем трём пользователям будут назначены одни и те же три роли: + + $roles = Role::factory()->count(3)->create(); + + $user = User::factory() + ->count(3) + ->hasAttached($roles, ['active' => true]) + ->create(); + + +#### Использование магических методов Many To Many + +Для удобства вы можете использовать магические методы отношений фабрики Laravel для построения отношений Many To Many. Например, в следующем примере будет использоваться соглашение, чтобы определить, что связанные модели должны быть созданы с помощью метода отношений `roles` модели `User`: + + $user = User::factory() + ->hasRoles(1, [ + 'name' => 'Editor' + ]) + ->create(); + + +### Полиморфные отношения + +[Полиморфные отношения](eloquent-relationships.md#polymorphic-relationships) также могут быть созданы с использованием фабрик. Полиморфные отношения Morph Many создаются так же, как типичные отношения Has Many. Например, если модель `App\Models\Post` имеет отношение `morphMany` с моделью `App\Models\Comment`: + + use App\Models\Post; + + $post = Post::factory()->hasComments(3)->create(); + + +#### Полиморфные отношения Morph To + +Магические методы нельзя использовать для создания отношений Morph To. Вместо этого метод `for` должен использоваться напрямую, а имя отношения должно быть явно указано. Например, представьте, что модель `Comment` имеет метод `commentable`, который определяет отношение Morph To. В этой ситуации мы можем создать три комментария, относящиеся к одному посту, используя напрямую метод `for`: + + $comments = Comment::factory()->count(3)->for( + Post::factory(), 'commentable' + )->create(); + + +#### Полиморфные отношения Many To Many + +Полиморфные отношения Many To Many (`morphToMany` / `morphedByMany`) могут быть созданы точно так же, как неполиморфные отношения Many To Many: + + use App\Models\Tag; + use App\Models\Video; + + $videos = Video::factory() + ->hasAttached( + Tag::factory()->count(3), + ['public' => true] + ) + ->create(); + +Конечно, магический метод `has` также используется для создания полиморфных отношений Many To Many: + + $videos = Video::factory() + ->hasTags(3, ['public' => true]) + ->create(); + + +### Определение отношений внутри фабрик + +Чтобы определить отношение в рамках вашей фабрики модели, вы обычно назначаете новый экземпляр фабрики внешнему ключу отношения. Обычно это делается для «обратных» отношений, таких как `belongsTo` и `morphTo`. Например, если вы хотите создать нового пользователя при создании публикации, вы можете сделать следующее: + + use App\Models\User; + + /** + * Определить состояние модели по умолчанию. + * + * @return array + */ + public function definition() + { + return [ + 'user_id' => User::factory(), + 'title' => fake()->title(), + 'content' => fake()->paragraph(), + ]; + } + +Если столбцы отношения зависят от фабрики, которая его определяет, то вы можете назначить замыкание атрибуту. Замыкание получит массив проанализированных атрибутов фабрики: + + /** + * Определить состояние модели по умолчанию. + * + * @return array + */ + public function definition() + { + return [ + 'user_id' => User::factory(), + 'user_type' => function (array $attributes) { + return User::find($attributes['user_id'])->type; + }, + 'title' => fake()->title(), + 'content' => fake()->paragraph(), + ]; + } + + +### Переиспользование существующей модели в отношениях + +Если у вас есть модели, которые имеют общие отношения с другой моделью, то вы можете использовать метод `recycle`, чтобы обеспечить повторное использование одного экземпляра связанной модели для всех отношений, созданных фабрикой. + +Например, представьте, что у вас есть модели `Airline`, `Flight` и `Ticket`, где билет принадлежит авиакомпании и рейсу, а рейс также принадлежит авиакомпании. При создании билетов вы, вероятно, захотите использовать одну и ту же авиакомпанию как для билета, так и для рейса, поэтому вы можете передать экземпляр авиакомпании методу `recycle`: + + Ticket::factory() + ->recycle(Airline::factory()->create()) + ->create(); + +Вы можете найти метод `recycle` особенно полезным, если у вас есть модели, принадлежащие одному пользователю или команде. + +Метод `recycle` также принимает коллекцию существующих моделей. Когда методу `recycle` передается коллекция, то из коллекции будет выбрана случайная модель, когда фабрике понадобится модель этого типа: + + Ticket::factory() + ->recycle($airlines) + ->create(); diff --git a/docs/eloquent-mutators.md b/docs/eloquent-mutators.md index c573e8e..196981f 100644 --- a/docs/eloquent-mutators.md +++ b/docs/eloquent-mutators.md @@ -64,7 +64,8 @@ $firstName = $user->first_name; -> {tip} Если вы хотите, чтобы эти вычисленные значения были добавлены к представлениям массива / JSON вашей модели, [вам нужно будет добавить их](eloquent-serialization.md#appending-values-to-json). +> **Примечание**\ +> Если вы хотите, чтобы эти вычисленные значения были добавлены к представлениям массива / JSON вашей модели, [вам нужно будет добавить их](eloquent-serialization.md#appending-values-to-json). #### Построение объекта-значения из нескольких атрибутов @@ -152,7 +153,6 @@ protected function address(): Attribute /** * Взаимодействие с именем пользователя. * - * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function firstName(): Attribute @@ -220,7 +220,7 @@ protected function address(): Attribute - `datetime` - `immutable_date` - `immutable_datetime` -- `decimal:`<digits> +- decimal:<precision> - `double` - `encrypted` - `encrypted:array` @@ -270,7 +270,8 @@ protected function address(): Attribute 'options' => 'object', ]); -> {note} Атрибуты, которые имеют значение `null`, не будут преобразованы. Кроме того, вы никогда не должны определять типизацию (или атрибут), имя которого совпадает с именем отношения. +> **Предупреждение**\ +> Атрибуты, которые имеют значение `null`, не будут преобразованы. Кроме того, вы никогда не должны определять типизацию (или атрибут), имя которого совпадает с именем отношения. #### Строковая типизация @@ -424,7 +425,8 @@ protected function address(): Attribute ### Типизация `Enum` -> {note} Перечисляемые типы доступны только в [PHP 8.1+](https://www.php.net/manual/ru/language.enumerations.php). +> **Предупреждение**\ +> Перечисляемые типы доступны только в [PHP 8.1+](https://www.php.net/manual/ru/language.enumerations.php). Eloquent также позволяет вам преобразовывать значения ваших атрибутов в [типизированные перечисления](https://www.php.net/manual/ru/language.enumerations.backed.php) PHP. Для этого вы можете указать атрибут, который вы хотите типизировать, и соответствующий класс перечисления в массиве `$casts` вашей модели: @@ -441,8 +443,8 @@ Eloquent также позволяет вам преобразовывать з После того, как вы определили типизацию в своей модели, указанный атрибут будет автоматически преобразован в перечисляемый тип и из него при взаимодействии с атрибутом: - if ($server->status == ServerStatus::provisioned) { - $server->status = ServerStatus::ready; + if ($server->status == ServerStatus::Provisioned) { + $server->status = ServerStatus::Ready; $server->save(); } @@ -486,9 +488,13 @@ Laravel шифрует строки, используя значение кон ## Пользовательская типизация -В Laravel есть множество встроенных полезных преобразователей; однако иногда требуется определить свои собственные. Вы можете добиться этого, определив класс, реализующий интерфейс `CastsAttributes`. +В Laravel есть множество встроенных полезных преобразователей; однако иногда требуется определить свои собственные. Чтобы создать класс типизации (типизатор), выполните команду `make:cast`. Новый типизатор будет помещен в ваш каталог `app/Casts`: + +```shell +php artisan make:cast Json +``` -Классы, реализующие этот интерфейс, должны определять методы `get` и `set`. Метод `get` отвечает за преобразование сырого значения из базы данных к типизированному значению, а метод `set` – должен преобразовывать типизированное значение в сырое значение, которое можно сохранить в базе данных. В качестве примера мы повторно реализуем встроенный преобразователь `json` как пользовательский типизатор: +Все пользовательские типизаторы реализуют интерфейс `CastsAttributes`. Классы, реализующие этот интерфейс, должны определять методы `get` и `set`. Метод `get` отвечает за преобразование сырого значения из базы данных к типизированному значению, а метод `set` – должен преобразовывать типизированное значение в сырое значение, которое можно сохранить в базе данных. В качестве примера мы повторно реализуем встроенный преобразователь `json` как пользовательский типизатор: save(); -> {tip} Если вы планируете сериализовать свои модели Eloquent, содержащие объекты-значения, в JSON или массивы, вам следует реализовать интерфейсы `Illuminate\Contracts\Support\Arrayable` и `JsonSerializable` для объекта-значения. +> **Примечание**\ +> Если вы планируете сериализовать свои модели Eloquent, содержащие объекты-значения, в JSON или массивы, вам следует реализовать интерфейсы `Illuminate\Contracts\Support\Arrayable` и `JsonSerializable` для объекта-значения. ### Сериализация в массив и JSON @@ -640,7 +647,15 @@ Laravel шифрует строки, используя значение кон ### Входящая типизация -Иногда требуется написать свой типизатор, который только преобразует указанные значения атрибутов модели, и не выполняет никаких операций при обращении к этим атрибутам. Классическим примером только входящей типизации является «хеширование». Пользовательские типизаторы только для входящих значений должны реализовывать интерфейс `CastsInboundAttributes`, требующий определение метода `set`. +Иногда требуется написать свой типизатор, который только преобразует указанные значения атрибутов модели, и не выполняет никаких операций при обращении к этим атрибутам. + +Пользовательские типизаторы только для входящих значений должны реализовывать интерфейс `CastsInboundAttributes`, требующий определение метода `set`. Для этого можно вызвать команду `make:cast` Artisan с параметром `--inbound`: + +```shell +php artisan make:cast Hash --inbound +``` + +Классическим примером только входящей типизации является «хеширование». Например, мы можем определить типизатор, который хэширует входящие значения с помощью определенного алгоритма: {note} Поскольку PostgreSQL не поддерживает выполнение функции `MAX` для столбцов UUID, в настоящее время невозможно использовать отношения «один-из-многих» в сочетании со столбцами UUID PostgreSQL. +> **Предупреждение**\ +> Поскольку PostgreSQL не поддерживает выполнение функции `MAX` для столбцов UUID, в настоящее время невозможно использовать отношения «один-из-многих» в сочетании со столбцами UUID PostgreSQL. #### Дополнения отношений Один из многих @@ -609,7 +611,8 @@ public function currentPricing() return $this->belongsToMany(Role::class)->withTimestamps(); -> {note} Промежуточные таблицы, использующие автоматически поддерживаемые временные метки Eloquent, должны иметь столбцы временных меток `created_at` и `updated_at`. +> **Предупреждение**\ +> Промежуточные таблицы, использующие автоматически поддерживаемые временные метки Eloquent, должны иметь столбцы временных меток `created_at` и `updated_at`. #### Корректировка имени атрибута `pivot` @@ -631,7 +634,7 @@ public function currentPricing() } -### Фильтрация запросов по столбцам сводной таблицы +### Запросы фильтрации по столбцам сводной таблицы Вы также можете отфильтровать результаты, возвращаемые запросами отношения `belongsToMany`, используя методы `wherePivot`, `wherePivotIn`, `wherePivotNotIn`, `wherePivotBetween`, `wherePivotNotBetween`, `wherePivotNull` и `wherePivotNotNull` при определении отношения: @@ -660,6 +663,15 @@ public function currentPricing() ->as('subscriptions') ->wherePivotNotNull('expired_at'); + +### Запросы упорядочивания по столбцам сводной таблицы + +Вы можете упорядочить результаты, возвращаемые запросами отношения `belongsToMany`, используя метод `orderByPivot`. В следующем примере мы получим все последние знаки отличия для пользователя: + + return $this->belongsToMany(Badge::class) + ->where('rank', 'gold') + ->orderByPivot('created_at', 'desc'); + ### Определение пользовательских моделей сводных таблиц @@ -697,7 +709,8 @@ public function currentPricing() // } -> {note} Сводные модели не могут использовать трейт `SoftDeletes`. Если вам нужно программно удалить сводные записи, подумайте о преобразовании вашей сводной модели в реальную модель Eloquent. +> **Предупреждение**\ +> Сводные модели не могут использовать трейт `SoftDeletes`. Если вам нужно программно удалить сводные записи, подумайте о преобразовании вашей сводной модели в реальную модель Eloquent. #### Пользовательские сводные модели и автоинкрементные идентификаторы @@ -950,7 +963,8 @@ public function bestImage() } ``` -> {tip} Можно построить более сложные отношения «один-из-многих». Для получения дополнительной информации обратитесь к [разделу Дополнения отношений Один из многих](#advanced-has-one-of-many-relationships) документации. +> **Примечание**\ +> Можно построить более сложные отношения «один-из-многих». Для получения дополнительной информации обратитесь к [разделу Дополнения отношений Один из многих](#advanced-has-one-of-many-relationships) документации. ### Многие ко многим (полиморфное) @@ -977,7 +991,8 @@ public function bestImage() taggable_id - integer taggable_type - string -> {tip} Прежде чем погрузиться в полиморфные отношения «многие-ко-многим», вам может быть полезно прочитать документацию по типичным [отношениям «многие-ко-многим»](#many-to-many). +> **Примечание**\ +> Прежде чем погрузиться в полиморфные отношения «многие-ко-многим», вам может быть полезно прочитать документацию по типичным [отношениям «многие-ко-многим»](#many-to-many). #### Структура модели отношения Многие ко многим (полиморфное) @@ -1086,7 +1101,8 @@ public function bestImage() $class = Relation::getMorphedModel($alias); -> {note} При добавлении «карты полиморфных типов» в существующее приложение каждое значение столбца `*_type` в вашей базе данных, которое все еще содержит полностью определенный класс, необходимо преобразовать в его псевдоним, указанный в «карте полиморфных типов». +> **Предупреждение**\ +> При добавлении «карты полиморфных типов» в существующее приложение каждое значение столбца `*_type` в вашей базе данных, которое все еще содержит полностью определенный класс, необходимо преобразовать в его псевдоним, указанный в «карте полиморфных типов». ## Динамические отношения @@ -1102,7 +1118,8 @@ public function bestImage() return $orderModel->belongsTo(Customer::class, 'customer_id'); }); -> {note} При определении динамических отношений всегда предоставляйте явные аргументы имени ключа методам связи Eloquent. +> **Предупреждение**\ +> При определении динамических отношений всегда предоставляйте явные аргументы имени ключа методам связи Eloquent. ## Запросы отношений @@ -1224,7 +1241,8 @@ where user_id = ? and (active = 1 or votes >= 100) $query->where('content', 'like', 'code%'); }, '>=', 10)->get(); -> {note} Eloquent в настоящее время не поддерживает запросы о существовании отношений между базами данных. Отношения должны существовать в одной базе данных. +> **Предупреждение**\ +> Eloquent в настоящее время не поддерживает запросы о существовании отношений между базами данных. Отношения должны существовать в одной базе данных. #### Однолинейная запись запроса, ограниченного наличием отношений @@ -1512,6 +1530,15 @@ select * from authors where id in (1, 2, 3, 4, 5, ...) $books = Book::with('author.contacts')->get(); +Кроме того, вы можете указать вложенные отношения, предоставив массив методу `with`, что может быть удобно при нетерпеливой загрузке нескольких вложенных отношений: + + $books = Book::with([ + 'author' => [ + 'contacts', + 'publisher', + ], + ])->get(); + #### Вложенная нетерпеливая загрузка отношений Morph To @@ -1554,7 +1581,8 @@ select * from authors where id in (1, 2, 3, 4, 5, ...) $books = Book::with('author:id,name,book_id')->get(); -> {note} При использовании этого функционала вы всегда должны включать столбец `id` и любые соответствующие столбцы внешнего ключа в список столбцов, которые вы хотите получить. +> **Предупреждение**\ +> При использовании этого функционала вы всегда должны включать столбец `id` и любые соответствующие столбцы внешнего ключа в список столбцов, которые вы хотите получить. #### Нетерпеливая загрузка по умолчанию @@ -1618,7 +1646,8 @@ select * from authors where id in (1, 2, 3, 4, 5, ...) $query->orderBy('created_at', 'desc'); }])->get(); -> {note} Методы `limit` и `take` построителя запросов нельзя использовать при ограничении нетерпеливой загрузки. +> **Предупреждение**\ +> Методы `limit` и `take` построителя запросов нельзя использовать при ограничении нетерпеливой загрузки. #### Ограничение нетерпеливой загрузки отношений Morph To @@ -1641,6 +1670,17 @@ select * from authors where id in (1, 2, 3, 4, 5, ...) В этом примере Eloquent будет загружать только те посты, которые не были скрыты, а видео только с типом как образовательное. + +#### Ограничение нетерпеливой загрузки существующих отношений + +Иногда требуется проверить наличие отношения, а также одновременно загрузить отношение на основе тех же условий. Например, необходимо получить модели `User`, только у которых есть дочерние модели `Post`, в соответствии с конкретным условием запроса, а также нетерпеливо загрузить соответствующие посты. Вы можете сделать это, используя метод `withWhereHas`: + + use App\Models\User; + + $users = User::withWhereHas('posts', function ($query) { + $query->where('featured', true); + })->get(); + ### Нетерпеливая пост-загрузка @@ -1804,7 +1844,8 @@ Eloquent содержит удобные методы для добавлени Вы также можете использовать методы `findOrNew`, `firstOrNew`, `firstOrCreate` и `updateOrCreate` для [создания и обновления моделей отношений](eloquent.md#upserts). -> {tip} Перед использованием метода `create` обязательно ознакомьтесь с документацией о [массовом присвоении](eloquent.md#mass-assignment) атрибутов. +> **Примечание**\ +> Перед использованием метода `create` обязательно ознакомьтесь с документацией о [массовом присвоении](eloquent.md#mass-assignment) атрибутов. ### Обновление отношений Один К @@ -1888,6 +1929,13 @@ Eloquent также содержит методы, которые делают $user->roles()->toggle([1, 2, 3]); +Вы также можете передать дополнительные значения сводной таблицы по идентификаторам: + + $user->roles()->toggle([ + 1 => ['expires' => true], + 2 => ['expires' => true], + ]); + #### Обновление записи сводной таблицы отношений Многие ко многим @@ -1930,4 +1978,5 @@ Eloquent также содержит методы, которые делают } } -> {note} Временные метки родительской модели будут обновлены только в том случае, если дочерняя модель обновлена с помощью метода `save` Eloquent. +> **Предупреждение**\ +> Временные метки родительской модели будут обновлены только в том случае, если дочерняя модель обновлена с помощью метода `save` Eloquent. diff --git a/docs/eloquent-resources.md b/docs/eloquent-resources.md index 85b84f5..66b5a02 100644 --- a/docs/eloquent-resources.md +++ b/docs/eloquent-resources.md @@ -44,7 +44,8 @@ php artisan make:resource UserCollection ## Обзор концепции -> {tip} Это лишь общий обзор ресурсов и коллекций ресурса. Мы настоятельно рекомендуем вам прочитать другие разделы этой документации, чтобы получить более глубокое понимание возможностей создания и настройки ресурса, предлагаемые вам. +> **Примечание**\ +> Это лишь общий обзор ресурсов и коллекций ресурса. Мы настоятельно рекомендуем вам прочитать другие разделы этой документации, чтобы получить более глубокое понимание возможностей создания и настройки ресурса, предлагаемые вам. Прежде чем углубляться во все варианты, доступные вам при написании ресурсов, давайте сначала рассмотрим, как ресурсы используются в Laravel. Класс ресурсов представляет собой единую модель, которую необходимо преобразовать в структуру JSON. Например, вот простой класс ресурса `UserResource`: @@ -195,7 +196,8 @@ php artisan make:resource UserCollection ## Написание ресурсов -> {tip} Если вы не читали [обзор концепции](#concept-overview), настоятельно рекомендуется сделать это, прежде чем приступить к работе с этой документацией. +> **Примечание**\ +> Если вы не читали [обзор концепции](#concept-overview), настоятельно рекомендуется сделать это, прежде чем приступить к работе с этой документацией. По сути, ресурсы просты. Им нужно только преобразовать переданную модель в массив. Итак, каждый ресурс содержит метод `toArray`, который переводит атрибуты вашей модели в удобный для API массив, который может быть возвращен из маршрутов или контроллеров вашего приложения: @@ -259,7 +261,8 @@ php artisan make:resource UserCollection ]; } -> {tip} Если вы хотите включить отношения только тогда, когда они уже загружены, ознакомьтесь с документацией по [условным отношениям](#conditional-relationships). +> **Примечание**\ +> Если вы хотите включить отношения только тогда, когда они уже загружены, ознакомьтесь с документацией по [условным отношениям](#conditional-relationships). #### Коллекции ресурса @@ -320,12 +323,12 @@ php artisan make:resource UserCollection { "id": 1, "name": "Eladio Schroeder Sr.", - "email": "therese28@example.com", + "email": "therese28@example.com" }, { "id": 2, "name": "Liliana Mayert", - "email": "evandervort@example.com", + "email": "evandervort@example.com" } ] } @@ -344,7 +347,7 @@ php artisan make:resource UserCollection /** * Обертка «данных», которую следует применить. * - * @var string + * @var string|null */ public static $wrap = 'user'; } @@ -381,7 +384,8 @@ php artisan make:resource UserCollection } } -> {note} Метод `withoutWrapping` влияет только на самый верхний уровень ответа и не удаляет ключи `data`, которые вы вручную добавляете в свои собственные коллекции ресурса. +> **Предупреждение**\ +> Метод `withoutWrapping` влияет только на самый верхний уровень ответа и не удаляет ключи `data`, которые вы вручную добавляете в свои собственные коллекции ресурса. #### Обертывание вложенных ресурсов @@ -421,12 +425,12 @@ php artisan make:resource UserCollection { "id": 1, "name": "Eladio Schroeder Sr.", - "email": "therese28@example.com", + "email": "therese28@example.com" }, { "id": 2, "name": "Liliana Mayert", - "email": "evandervort@example.com", + "email": "evandervort@example.com" } ], "links":{ @@ -467,12 +471,12 @@ php artisan make:resource UserCollection { "id": 1, "name": "Eladio Schroeder Sr.", - "email": "therese28@example.com", + "email": "therese28@example.com" }, { "id": 2, "name": "Liliana Mayert", - "email": "evandervort@example.com", + "email": "evandervort@example.com" } ], "links":{ @@ -524,6 +528,10 @@ php artisan make:resource UserCollection return 'secret-value'; }), +Метод `whenHas` может использоваться для включения атрибута, если он действительно присутствует в базовой модели: + + 'name' => $this->whenHas('name'), + Кроме того, метод `whenNotNull` может использоваться для включения атрибута в ответ ресурса, если атрибут не равен `null`: 'name' => $this->whenNotNull($this->name), @@ -556,7 +564,8 @@ php artisan make:resource UserCollection Опять же, если переданное условие равносильно `false`, то эти атрибуты будут удалены из ответа ресурса перед его отправкой клиенту. -> {note} Метод `mergeWhen` не следует использовать в массивах, в которых смешиваются строковые и числовые ключи. Кроме того, его не следует использовать в массивах с цифровыми ключами, которые не упорядочены последовательно. +> **Предупреждение**\ +> Метод `mergeWhen` не следует использовать в массивах, в которых смешиваются строковые и числовые ключи. Кроме того, его не следует использовать в массивах с цифровыми ключами, которые не упорядочены последовательно. ### Условные отношения @@ -587,6 +596,35 @@ php artisan make:resource UserCollection В этом примере, если отношение не было загружено, ключ `posts` будет удален из ответа ресурса перед его отправкой клиенту. + +#### Условная загрузка количества отношений + +В дополнение к условному включению отношений вы можете условно включать «количество» отношений в ответы вашего ресурса в зависимости от того, было ли количество отношений загружено в модель: + + new UserResource($user->loadCount('posts')); + +Метод `whenCounted` может использоваться для условного включения количества отношений в ваш ответ ресурса. Этот метод позволяет избежать ненужного включения атрибута, если счетчик отношений отсутствует: + + /** + * Преобразовать ресурс в массив. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function toArray($request) + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'posts_count' => $this->whenCounted('posts'), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } + +В этом примере, если счетчик отношений `posts` не был загружен, то ключ `posts_count` будет удален из ответа ресурса до того, как он будет отправлен клиенту. + #### Условная сводная информация diff --git a/docs/eloquent-serialization.md b/docs/eloquent-serialization.md index fd76670..3d6f1fb 100644 --- a/docs/eloquent-serialization.md +++ b/docs/eloquent-serialization.md @@ -13,7 +13,8 @@ При создании API-интерфейсов с использованием Laravel вам часто нужно преобразовывать свои модели и отношения в массивы или JSON. Eloquent включает удобные методы для выполнения этих преобразований, а также для управления тем, какие атрибуты включаются в сериализованное представление ваших моделей. -> {tip} Чтобы получить еще более надежный способ обработки JSON-сериализации модели Eloquent и коллекции, ознакомьтесь с документацией на [Ресурсы API Eloquent](eloquent-resources.md). +> **Примечание**\ +> Чтобы получить еще более надежный способ обработки JSON-сериализации модели Eloquent и коллекции, ознакомьтесь с документацией на [Ресурсы API Eloquent](eloquent-resources.md). ## Сериализация моделей и коллекций @@ -90,7 +91,8 @@ protected $hidden = ['password']; } -> {tip} Чтобы скрыть отношения, добавьте имя метода-отношения к свойству `$hidden` модели Eloquent. +> **Примечание**\ +> Чтобы скрыть отношения, добавьте имя метода-отношения к свойству `$hidden` модели Eloquent. В качестве альтернативы вы можете использовать свойство `visible` для определения «разрешенного списка» атрибутов, которые должны быть включены в массив модели и представление JSON. Все атрибуты, отсутствующие в массиве `$visible`, будут скрыты при преобразовании модели в массив или JSON: @@ -117,10 +119,16 @@ return $user->makeVisible('attribute')->toArray(); -Аналогично, если вы хотите скрыть некоторые атрибуты, которые обычно видны, вы можете использовать метод `makeHidden`: +Аналогично, если вы хотите скрыть некоторые атрибуты, которые обычно видны, то вы можете использовать метод `makeHidden`: return $user->makeHidden('attribute')->toArray(); +Если вы хотите временно переопределить все видимые или скрытые атрибуты, то вы можете использовать методы `setVisible` и `setHidden` соответственно: + + return $user->setVisible(['id', 'name'])->toArray(); + + return $user->setHidden(['email', 'password', 'remember_token'])->toArray(); + ## Добавление значений в JSON diff --git a/docs/eloquent.md b/docs/eloquent.md index daf155a..ee82e6d 100644 --- a/docs/eloquent.md +++ b/docs/eloquent.md @@ -5,9 +5,11 @@ - [Соглашения по именованию моделей Eloquent](#eloquent-model-conventions) - [Именование таблиц](#table-names) - [Первичные ключи](#primary-keys) + - [Ключи UUID и ULID](#uuid-and-ulid-keys) - [Временные метки](#timestamps) - [Соединения с БД](#database-connections) - [Значения атрибутов по умолчанию](#default-attribute-values) + - [Изменение «строгости» Eloquent](#configuring-eloquent-strictness) - [Получение моделей](#retrieving-models) - [Коллекции](#collections) - [Разбиение результатов](#chunking-results) @@ -41,7 +43,12 @@ Laravel содержит библиотеку Eloquent ORM (объектно-реляционное отображение), которая позволяет с удовольствием взаимодействовать с базой данных. При использовании Eloquent каждая таблица БД имеет соответствующую «Модель», которая используется для взаимодействия с этой таблицей. Помимо получения записей из таблицы БД, модели Eloquent также позволяют вставлять, обновлять и удалять записи из таблицы. -> {tip} Перед началом работы обязательно настройте соединение с БД в конфигурационном файле `config/database.php`. Для получения дополнительной информации о настройке БД ознакомьтесь с [документацией по конфигурированию БД](database.md#configuration). +> **Примечание**\ +> Перед началом работы обязательно настройте соединение с БД в конфигурационном файле `config/database.php`. Для получения дополнительной информации о настройке БД ознакомьтесь с [документацией по конфигурированию БД](database.md#configuration). + +#### Laravel Bootcamp + +Если вы новичок в Laravel, не стесняйтесь перейти в [Laravel Bootcamp](https://bootcamp.laravel.com). Laravel Bootcamp проведет вас через создание вашего первого приложения Laravel с использованием Eloquent. Это отличный способ ознакомиться со всем, что могут предложить Laravel и Eloquent. ## Генерация классов модели @@ -90,6 +97,15 @@ php artisan make:model Flight --all php artisan make:model Member --pivot ``` + +#### Просмотр сведений о моделях + +Иногда бывает сложно определить все доступные атрибуты и отношения модели, просто просмотрев ее код. Вместо этого попробуйте команду `model:show` Artisan, которая предоставляет удобный обзор всех атрибутов и отношений модели: + +```shell +php artisan model:show Flight +``` + ## Соглашения по именованию моделей Eloquent @@ -183,6 +199,69 @@ Eloquent также предполагает, что в соответствую Eloquent требует, чтобы каждая модель имела по крайней мере один однозначно идентифицирующий «ID», который может служить ее первичным ключом. «Составные» первичные ключи не поддерживаются моделями Eloquent. Однако вы можете добавить дополнительные многоколоночные уникальные индексы к таблицам базы данных в дополнение к однозначно определяющему (уникальному) первичному ключу таблицы. + +### Ключи UUID и ULID + +Вместо использования автоинкрементных целочисленных первичных ключей вашей модели Eloquent вы можете использовать UUID. UUID — это универсальные уникальные буквенно-цифровые идентификаторы длиной 36 символов. + +Если вы хотите, чтобы модель использовала ключ UUID, то вы можете использовать трейт `Illuminate\Database\Eloquent\Concerns\HasUuids` в модели. Конечно, вы должны убедиться, что в модели есть [эквивалентный UUID столбец первичного ключа](migrations.md#column-method-uuid): + + use Illuminate\Database\Eloquent\Concerns\HasUuids; + use Illuminate\Database\Eloquent\Model; + + class Article extends Model + { + use HasUuids; + + // ... + } + + $article = Article::create(['title' => 'Traveling to Europe']); + + $article->id; // "8f8e8478-9035-4d23-b9a7-62f4d2612ce5" + +По умолчанию трейт `HasUuids` будет генерировать [«упорядоченные» UUID](helpers.md#method-str-ordered-uuid) для ваших моделей. Эти UUID более эффективны для хранения индексированных баз данных, поскольку их можно лексикографически сортировать. + +Вы можете переопределить процесс генерации UUID для конкретной модели, определив в модели метод `newUniqueId`. Кроме того, вы можете указать, какие столбцы должны принимать UUID, определив метод `uniqueIds` в модели: + + use Ramsey\Uuid\Uuid; + + /** + * Создать новый UUID для модели. + * + * @return string + */ + public function newUniqueId() + { + return (string) Uuid::uuid4(); + } + + /** + * Получить столбцы, которые должны принимать уникальный идентификатор. + * + * @return array + */ + public function uniqueIds() + { + return ['id', 'discount_code']; + } + +При желании вы можете использовать «ULID» вместо UUID. ULID аналогичны UUID; однако их длина составляет всего 26 символов. Как и упорядоченные UUID, ULID можно лексикографически сортировать для эффективного индексирования базы данных. Чтобы задействовать ULID, вы должны использовать трейт `Illuminate\Database\Eloquent\Concerns\HasUlids` в своей модели. Вы также должны убедиться, что модель имеет [эквивалентный ULID столбец первичного ключа](migrations.md#column-method-ulid): + + use Illuminate\Database\Eloquent\Concerns\HasUlids; + use Illuminate\Database\Eloquent\Model; + + class Article extends Model + { + use HasUlids; + + // ... + } + + $article = Article::create(['title' => 'Traveling to Asia']); + + $article->id; // "01gd4d3tgrrfqeda94gdbtdk5c" + ### Временные метки @@ -232,6 +311,10 @@ Eloquent требует, чтобы каждая модель имела по к const UPDATED_AT = 'updated_date'; } +Если вы хотите выполнять операции с моделью без изменения временной метки `updated_at` модели, то необходимо работать с моделью в замыкании, переданном методу `withoutTimestamps`: + + Model::withoutTimestamps(fn () => $post->increment(['reads'])); + ### Соединения с БД @@ -276,6 +359,48 @@ Eloquent требует, чтобы каждая модель имела по к ]; } + +### Изменение «строгости» Eloquent + +Laravel предлагает несколько методов, позволяющих изменить поведение и «строгость» Eloquent в различных ситуациях. + +Во-первых, метод `preventLazyLoading` принимает необязательный логический аргумент, который указывает, следует ли предотвращать отложенную загрузку. Например, вы можете предотвращать отложенную загрузку только в не эксплуатационных окружениях, чтобы ваше приложение в эксплуатационном окружении продолжало нормально функционировать, даже если в коде случайно присутствует отложенная загрузка отношения. Как правило, вызов этого метода осуществляется в методе `boot` поставщика `App\Providers\AppServiceProvider`. + +```php +use Illuminate\Database\Eloquent\Model; + +/** + * Загрузка любых служб приложения. + * + * @return void + */ +public function boot() +{ + Model::preventLazyLoading(! $this->app->isProduction()); +} +``` + +Кроме того, вы можете указать Laravel генерировать исключение при попытке массового присвоения значений для неразрешенных атрибутов, вызвав метод `preventSilentlyDiscardingAttributes`. Это может помочь предотвратить непредвиденные ошибки во время локальной разработки при попытке установить атрибут, который не был добавлен в массив `fillable` модели: + +```php +Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction()); +``` + +Наконец, вы можете указать Eloquent генерировать исключение, если вы попытаетесь получить доступ к атрибуту модели, когда этот атрибут фактически не был извлечен из базы данных или когда атрибут не существует. Например, это может произойти, если вы забыли добавить атрибут в выражение `select` запроса Eloquent: + +```php +Model::preventAccessingMissingAttributes(! $this->app->isProduction()); +``` + + +#### Включение «строгого режима» Eloquent + +Для удобства вы можете включить все три рассмотренных выше метода, просто вызвав метод `shouldBeStrict`: + +```php +Model::shouldBeStrict(! $this->app->isProduction()); +``` + ## Получение моделей @@ -297,7 +422,8 @@ Eloquent требует, чтобы каждая модель имела по к ->take(10) ->get(); -> {tip} Поскольку модель Eloquent является построителем запросов, вам следует просмотреть все методы, предлагаемые [построителем запросов](queries.md). Вы можете использовать любой из этих методов при написании запросов Eloquent. +> **Примечание**\ +> Поскольку модель Eloquent является построителем запросов, вам следует просмотреть все методы, предлагаемые [построителем запросов](queries.md). Вы можете использовать любой из этих методов при написании запросов Eloquent. #### Обновление моделей @@ -401,7 +527,8 @@ Flight::where('departed', true) Метод `cursor` выполнит только один запрос к БД; однако отдельные модели Eloquent не будут включены в результирующий набор, пока они не будут фактически итерированы. Следовательно, только одна модель Eloquent хранится в памяти в любой момент времени при итерации с использованием курсора. -> {note} Поскольку метод `cursor` всегда хранит в памяти только одну модель Eloquent, то нетерпеливая загрузка отношений недопустима. Если вам нужно нетерпеливо загрузить отношения, то рассмотрите возможность использования метода [`lazy`](#chunking-using-lazy-collections). +> **Предупреждение**\ +> Поскольку метод `cursor` всегда хранит в памяти только одну модель Eloquent, то нетерпеливая загрузка отношений недопустима. Если вам нужно нетерпеливо загрузить отношения, то рассмотрите возможность использования метода [`lazy`](#chunking-using-lazy-collections). Внутри метод `cursor` использует [генераторы PHP](https://www.php.net/manual/ru/language.generators.overview.php) для реализации этого функционала: @@ -615,7 +742,8 @@ Eloquent также предлагает поддержку расширенны Метод `update` ожидает массив пар ключей и значений, представляющих столбцы, которые должны быть обновлены. Метод `update` возвращает количество затронутых строк. -> {note} События модели Eloquent `saving`, `saved`, `updating` и `updated` при массовом обновлении **не будут инициированы** для затронутых моделей. Это связано с тем, что модели фактически никогда не извлекаются при массовом обновлении. +> **Предупреждение**\ +> События модели Eloquent `saving`, `saved`, `updating` и `updated` при массовом обновлении **не будут инициированы** для затронутых моделей. Это связано с тем, что модели фактически никогда не извлекаются при массовом обновлении. #### Изучение изменений атрибутов @@ -747,6 +875,25 @@ Eloquent содержит методы `isDirty`, `isClean` и `wasChanged` дл */ protected $guarded = []; + +#### Исключения массового присвоения + +По умолчанию атрибуты, не включенные в массив `$fillable`, молча отбрасываются при выполнении операций массового присваивания. В эксплуатационном окружении это ожидаемое поведение; однако во время локальной разработки это может привести к путанице в отношении того, почему изменения модели не вступают в силу. + +Если вы хотите, то вы можете указать Laravel генерировать исключение при попытке массового присвоения значений для неразрешенных атрибутов, вызвав метод `preventSilentlyDiscardingAttributes`. Как правило, вызов этого метода осуществляется в методе `boot` одного из поставщика служб вашего приложения: + + use Illuminate\Database\Eloquent\Model; + + /** + * Загрузка любых служб приложения. + * + * @return void + */ + public function boot() + { + Model::preventSilentlyDiscardingAttributes($this->app->isLocal()); + } + ### Обновления-вставки @@ -766,7 +913,8 @@ Eloquent содержит методы `isDirty`, `isClean` и `wasChanged` дл ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] ], ['departure', 'destination'], ['price']); -> {note} Все базы данных, кроме SQL Server, требуют, чтобы столбцы во втором аргументе метода `upsert` имели «первичный» или «уникальный» индекс. Вдобавок, драйвер базы данных MySQL игнорирует второй аргумент метода `upsert` и всегда использует «первичный» и «уникальный» индексы таблицы для обнаружения существующих записей. +> **Предупреждение**\ +> Все базы данных, кроме SQL Server, требуют, чтобы столбцы во втором аргументе метода `upsert` имели «первичный» или «уникальный» индекс. Вдобавок, драйвер базы данных MySQL игнорирует второй аргумент метода `upsert` и всегда использует «первичный» и «уникальный» индексы таблицы для обнаружения существующих записей. ## Удаление моделей @@ -796,7 +944,8 @@ Eloquent содержит методы `isDirty`, `isClean` и `wasChanged` дл Flight::destroy(collect([1, 2, 3])); -> {note} Метод `destroy` загружает каждую модель отдельно и вызывает для них метод `delete`, чтобы сработали события `deleting` и `deleted` должным образом для каждой модели. +> **Предупреждение**\ +> Метод `destroy` загружает каждую модель отдельно и вызывает для них метод `delete`, чтобы сработали события `deleting` и `deleted` должным образом для каждой модели. #### Удаление моделей через запрос @@ -805,7 +954,8 @@ Eloquent содержит методы `isDirty`, `isClean` и `wasChanged` дл $deleted = Flight::where('active', 0)->delete(); -> {note} События модели Eloquent `deleting` и `deleted` при массовом удалении не будут инициированы для удаленных моделей. Это связано с тем, что модели фактически не извлекаются при выполнении оператора `delete`. +> **Предупреждение**\ +> События модели Eloquent `deleting` и `deleted` при массовом удалении не будут инициированы для удаленных моделей. Это связано с тем, что модели фактически не извлекаются при выполнении оператора `delete`. ### Программное удаление @@ -824,7 +974,8 @@ Eloquent содержит методы `isDirty`, `isClean` и `wasChanged` дл use SoftDeletes; } -> {tip} Трейт `SoftDeletes` автоматически типизирует атрибут `deleted_at` к экземпляру `DateTime` / `Carbon`. +> **Примечание**\ +> Трейт `SoftDeletes` автоматически типизирует атрибут `deleted_at` к экземпляру `DateTime` / `Carbon`. Вам также следует добавить столбец `deleted_at` в таблицу БД. [Построитель схемы](migrations.md) Laravel содержит метод для создания этого столбца: @@ -972,7 +1123,8 @@ Eloquent содержит методы `isDirty`, `isClean` и `wasChanged` дл php artisan model:prune --pretend ``` -> {note} Программно удаляемые модели будут удалены (`forceDelete`) без возможности восстановления, если они соответствуют запросу сокращения. +> **Предупреждение**\ +> Программно удаляемые модели будут удалены (`forceDelete`) без возможности восстановления, если они соответствуют запросу очистки. #### Массовая очистка устаревших моделей @@ -1053,7 +1205,7 @@ php artisan model:prune --pretend {tip} Если ваша глобальная область запроса добавляет столбцы в выражение `SELECT` запроса, то вы должны использовать метод `addSelect` вместо `select`. Это предотвратит непреднамеренную замену существующего выражения `SELECT` запроса. +> **Примечание**\ +> Если ваша глобальная область запроса добавляет столбцы в выражение `SELECT` запроса, то вы должны использовать метод `addSelect` вместо `select`. Это предотвратит непреднамеренную замену существующего выражения `SELECT` запроса. #### Применение глобальных областей запроса @@ -1085,7 +1238,7 @@ php artisan model:prune --pretend namespace App\Models; - use App\Scopes\AncientScope; + use App\Models\Scopes\AncientScope; use Illuminate\Database\Eloquent\Model; class User extends Model @@ -1264,7 +1417,8 @@ Eloquent также позволяет вам определять глобал ## События -> {tip} Хотите транслировать события Eloquent прямо в клиентское приложение? Посмотрите [трансляцию событий модели](broadcasting.md#model-broadcasting) Laravel. +> **Примечание**\ +> Хотите транслировать события Eloquent прямо в клиентское приложение? Посмотрите [трансляцию событий модели](broadcasting.md#model-broadcasting) Laravel. Модели Eloquent инициируют некоторые события, что позволяет использовать следующие хуки жизненного цикла модели: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `trashed`, `forceDeleted`, `restoring`, `restored` и `replicating`. @@ -1279,6 +1433,7 @@ Eloquent также позволяет вам определять глобал use App\Events\UserDeleted; use App\Events\UserSaved; use Illuminate\Foundation\Auth\User as Authenticatable; + use Illuminate\Notifications\Notifiable; class User extends Authenticatable { @@ -1297,7 +1452,8 @@ Eloquent также позволяет вам определять глобал После определения и сопоставления событий вы можете использовать [слушателей событий](events.md#defining-listeners) для их обработки. -> {note} События модели Eloquent `saved`, `updated`, `deleting` и `deleted` при массовом обновлении или удалении **не будут инициированы** для затронутых моделей. Это связано с тем, что модели фактически не извлекаются при массовом обновлении или удалении. +> **Предупреждение**\ +> События модели Eloquent `saved`, `updated`, `deleting` и `deleted` при массовом обновлении или удалении **не будут инициированы** для затронутых моделей. Это связано с тем, что модели фактически не извлекаются при массовом обновлении или удалении. ### Использование замыканий @@ -1440,7 +1596,8 @@ php artisan make:observer UserObserver --model=User User::class => [UserObserver::class], ]; -> {tip} Наблюдатель может прослушивать дополнительные события, такие как `saving` и `retrieved`. Эти события описаны в документации [событий](#events). +> **Примечание**\ +> Наблюдатель может прослушивать дополнительные события, такие как `saving` и `retrieved`. Эти события описаны в документации [событий](#events). #### Наблюдатели и транзакции базы данных @@ -1481,7 +1638,7 @@ php artisan make:observer UserObserver --model=User use App\Models\User; - $user = User::withoutEvents(function () use () { + $user = User::withoutEvents(function () { User::findOrFail(1)->delete(); return User::find(2); @@ -1497,3 +1654,9 @@ php artisan make:observer UserObserver --model=User $user->name = 'Victoria Faith'; $user->saveQuietly(); + +Вы также можете «обновить», «удалить», «программно удалить», «восстановить» и «тиражировать» конкретную модель без инициации каких-либо событий: + + $user->deleteQuietly(); + + $user->restoreQuietly(); diff --git a/docs/errors.md b/docs/errors.md index 8e76c64..8544447 100644 --- a/docs/errors.md +++ b/docs/errors.md @@ -57,7 +57,8 @@ return false; }); -> {tip} Чтобы настроить отчет об исключениях для переданного исключения, вы можете рассмотреть возможность использования [отчетных исключений](#renderable-exceptions). +> **Примечание**\ +> Чтобы настроить отчет об исключениях для переданного исключения, вы можете рассмотреть возможность использования [отчетных исключений](#renderable-exceptions). #### Глобальное содержимое журнала @@ -155,7 +156,8 @@ InvalidOrderException::class, ]; -> {tip} За кулисами Laravel уже игнорирует для вас некоторые типы ошибок, такие как исключения, возникающие из-за ошибок 404 HTTP «не найдено» или 419 HTTP-ответ, сгенерированный при недопустимом токене CSRF. +> **Примечание**\ +> За кулисами Laravel уже игнорирует для вас некоторые типы ошибок, такие как исключения, возникающие из-за ошибок 404 HTTP «не найдено» или 419 HTTP-ответ, сгенерированный при недопустимом токене CSRF. ### Отображение исключений @@ -262,7 +264,8 @@ return false; } -> {tip} Вы можете указать любые требуемые зависимости метода `report`, и они будут автоматически внедрены в метод [контейнером служб](container.md) Laravel. +> **Примечание**\ +> Вы можете указать любые требуемые зависимости метода `report`, и они будут автоматически внедрены в метод [контейнером служб](container.md) Laravel. ## HTTP-исключения diff --git a/docs/events.md b/docs/events.md index 3f7df27..68d6918 100644 --- a/docs/events.md +++ b/docs/events.md @@ -42,7 +42,8 @@ ], ]; -> {tip} Команда `event:list` используется для отображения списка всех событий и слушателей, зарегистрированных вашим приложением. +> **Примечание**\ +> Команда `event:list` используется для отображения списка всех событий и слушателей, зарегистрированных вашим приложением. ### Генерация событий и слушателей @@ -264,7 +265,8 @@ Laravel находит слушателей событий, сканируя к } } -> {tip} В конструкторе ваших слушателей событий могут быть объявлены любые необходимые типы зависимостей. Все слушатели событий разрешаются через [контейнер служб](container.md) Laravel, поэтому зависимости будут внедрены автоматически. +> **Примечание**\ +> В конструкторе ваших слушателей событий могут быть объявлены любые необходимые типы зависимостей. Все слушатели событий разрешаются через [контейнер служб](container.md) Laravel, поэтому зависимости будут внедрены автоматически. #### Остановка распространения события @@ -439,7 +441,8 @@ Laravel находит слушателей событий, сканируя к public $afterCommit = true; } -> {tip} Чтобы узнать больше о том, как обойти эти проблемы, просмотрите документацию, касающуюся [заданий в очереди и транзакций базы данных](queues.md#jobs-and-database-transactions). +> **Примечание**\ +> Чтобы узнать больше о том, как обойти эти проблемы, просмотрите документацию, касающуюся [заданий в очереди и транзакций базы данных](queues.md#jobs-and-database-transactions). ### Обработка невыполненных заданий @@ -559,7 +562,8 @@ Laravel находит слушателей событий, сканируя к OrderShipped::dispatchUnless($condition, $order); -> {tip} При тестировании может быть полезно утверждать, что определенные события были отправлены без фактического запуска их слушателей. [Встроенные помощники по тестированию](mocking.md#event-fake) Laravel делает его легко. +> **Примечание**\ +> При тестировании может быть полезно утверждать, что определенные события были отправлены без фактического запуска их слушателей. [Встроенные помощники по тестированию](mocking.md#event-fake) Laravel делает его легко. ## Подписчики событий diff --git a/docs/facades.md b/docs/facades.md index 84ce64b..1a6e3fc 100644 --- a/docs/facades.md +++ b/docs/facades.md @@ -13,7 +13,7 @@ В документации Laravel вы увидите примеры кода, демонстрирующего взаимодействия с функционалом Laravel через «фасады». Фасады предоставляют «статический» интерфейс для классов, доступных в [контейнере служб](container.md) приложения. Laravel из коробки включает множество фасадов, обеспечивающих доступ почти ко всему функционалу Laravel. -Фасады Laravel служат «статическими прокси» для базовых классов в контейнере служб, обеспечивая преимущества краткого, выразительного синтаксиса при сохранении большей тестируемости и гибкости, чем традиционные статические методы. Если вы не совсем понимаете, как фасады работают под капотом – просто продолжайте изучать Laravel. +Фасады Laravel служат «статическими прокси» для базовых классов в контейнере служб, обеспечивая преимущества краткого, выразительного синтаксиса при сохранении большей тестируемости и гибкости, чем традиционные статические методы. Если вы не совсем понимаете, как фасады работают – просто продолжайте изучать Laravel. Все фасады Laravel определены в пространстве имён `Illuminate\Support\Facades`. Таким образом, мы можем легко получить доступ к такому фасаду: @@ -102,7 +102,7 @@ return cache('key'); }); -Под капотом помощник `cache` будет вызывать метод `get` класса, образующего фасад `Cache`. Итак, даже если мы используем глобальный помощник, мы можем написать следующий тест, чтобы убедиться, что метод был вызван с ожидаемым аргументом: +Помощник `cache` будет вызывать метод `get` класса, образующего фасад `Cache`. Итак, даже если мы используем глобальный помощник, мы можем написать следующий тест, чтобы убедиться, что метод был вызван с ожидаемым аргументом: use Illuminate\Support\Facades\Cache; @@ -257,50 +257,51 @@ Фасад | Класс | Привязка в контейнере служб ------------- | ------------- | ------------- -App | [Illuminate\Foundation\Application](https://laravel.com/api/8.x/Illuminate/Foundation/Application.html) | `app` -Artisan | [Illuminate\Contracts\Console\Kernel](https://laravel.com/api/8.x/Illuminate/Contracts/Console/Kernel.html) | `artisan` -Auth | [Illuminate\Auth\AuthManager](https://laravel.com/api/8.x/Illuminate/Auth/AuthManager.html) | `auth` -Auth (Instance) | [Illuminate\Contracts\Auth\Guard](https://laravel.com/api/8.x/Illuminate/Contracts/Auth/Guard.html) | `auth.driver` -Blade | [Illuminate\View\Compilers\BladeCompiler](https://laravel.com/api/8.x/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` -Broadcast | [Illuminate\Contracts\Broadcasting\Factory](https://laravel.com/api/8.x/Illuminate/Contracts/Broadcasting/Factory.html) |   -Broadcast (Instance) | [Illuminate\Contracts\Broadcasting\Broadcaster](https://laravel.com/api/8.x/Illuminate/Contracts/Broadcasting/Broadcaster.html) |   -Bus | [Illuminate\Contracts\Bus\Dispatcher](https://laravel.com/api/8.x/Illuminate/Contracts/Bus/Dispatcher.html) |   -Cache | [Illuminate\Cache\CacheManager](https://laravel.com/api/8.x/Illuminate/Cache/CacheManager.html) | `cache` -Cache (Instance) | [Illuminate\Cache\Repository](https://laravel.com/api/8.x/Illuminate/Cache/Repository.html) | `cache.store` -Config | [Illuminate\Config\Repository](https://laravel.com/api/8.x/Illuminate/Config/Repository.html) | `config` -Cookie | [Illuminate\Cookie\CookieJar](https://laravel.com/api/8.x/Illuminate/Cookie/CookieJar.html) | `cookie` -Crypt | [Illuminate\Encryption\Encrypter](https://laravel.com/api/8.x/Illuminate/Encryption/Encrypter.html) | `encrypter` -Date | [Illuminate\Support\DateFactory](https://laravel.com/api/8.x/Illuminate/Support/DateFactory.html) | `date` -DB | [Illuminate\Database\DatabaseManager](https://laravel.com/api/8.x/Illuminate/Database/DatabaseManager.html) | `db` -DB (Instance) | [Illuminate\Database\Connection](https://laravel.com/api/8.x/Illuminate/Database/Connection.html) | `db.connection` -Event | [Illuminate\Events\Dispatcher](https://laravel.com/api/8.x/Illuminate/Events/Dispatcher.html) | `events` -File | [Illuminate\Filesystem\Filesystem](https://laravel.com/api/8.x/Illuminate/Filesystem/Filesystem.html) | `files` -Gate | [Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/8.x/Illuminate/Contracts/Auth/Access/Gate.html) |   -Hash | [Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/8.x/Illuminate/Contracts/Hashing/Hasher.html) | `hash` -Http | [Illuminate\Http\Client\Factory](https://laravel.com/api/8.x/Illuminate/Http/Client/Factory.html) |   -Lang | [Illuminate\Translation\Translator](https://laravel.com/api/8.x/Illuminate/Translation/Translator.html) | `translator` -Log | [Illuminate\Log\LogManager](https://laravel.com/api/8.x/Illuminate/Log/LogManager.html) | `log` -Mail | [Illuminate\Mail\Mailer](https://laravel.com/api/8.x/Illuminate/Mail/Mailer.html) | `mailer` -Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/api/8.x/Illuminate/Notifications/ChannelManager.html) |   -Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/8.x/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` -Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/8.x/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` -Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/8.x/Illuminate/Queue/QueueManager.html) | `queue` -Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/8.x/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` -Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/8.x/Illuminate/Queue/Queue.html) |   -Redirect | [Illuminate\Routing\Redirector](https://laravel.com/api/8.x/Illuminate/Routing/Redirector.html) | `redirect` -Redis | [Illuminate\Redis\RedisManager](https://laravel.com/api/8.x/Illuminate/Redis/RedisManager.html) | `redis` -Redis (Instance) | [Illuminate\Redis\Connections\Connection](https://laravel.com/api/8.x/Illuminate/Redis/Connections/Connection.html) | `redis.connection` -Request | [Illuminate\Http\Request](https://laravel.com/api/8.x/Illuminate/Http/Request.html) | `request` -Response | [Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/8.x/Illuminate/Contracts/Routing/ResponseFactory.html) |   -Response (Instance) | [Illuminate\Http\Response](https://laravel.com/api/8.x/Illuminate/Http/Response.html) |   -Route | [Illuminate\Routing\Router](https://laravel.com/api/8.x/Illuminate/Routing/Router.html) | `router` -Schema | [Illuminate\Database\Schema\Builder](https://laravel.com/api/8.x/Illuminate/Database/Schema/Builder.html) |   -Session | [Illuminate\Session\SessionManager](https://laravel.com/api/8.x/Illuminate/Session/SessionManager.html) | `session` -Session (Instance) | [Illuminate\Session\Store](https://laravel.com/api/8.x/Illuminate/Session/Store.html) | `session.store` -Storage | [Illuminate\Filesystem\FilesystemManager](https://laravel.com/api/8.x/Illuminate/Filesystem/FilesystemManager.html) | `filesystem` -Storage (Instance) | [Illuminate\Contracts\Filesystem\Filesystem](https://laravel.com/api/8.x/Illuminate/Contracts/Filesystem/Filesystem.html) | `filesystem.disk` -URL | [Illuminate\Routing\UrlGenerator](https://laravel.com/api/8.x/Illuminate/Routing/UrlGenerator.html) | `url` -Validator | [Illuminate\Validation\Factory](https://laravel.com/api/8.x/Illuminate/Validation/Factory.html) | `validator` -Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/api/8.x/Illuminate/Validation/Validator.html) |   -View | [Illuminate\View\Factory](https://laravel.com/api/8.x/Illuminate/View/Factory.html) | `view` -View (Instance) | [Illuminate\View\View](https://laravel.com/api/8.x/Illuminate/View/View.html) |   +App | [Illuminate\Foundation\Application](https://laravel.com/api/9.x/Illuminate/Foundation/Application.html) | `app` +Artisan | [Illuminate\Contracts\Console\Kernel](https://laravel.com/api/9.x/Illuminate/Contracts/Console/Kernel.html) | `artisan` +Auth | [Illuminate\Auth\AuthManager](https://laravel.com/api/9.x/Illuminate/Auth/AuthManager.html) | `auth` +Auth (Instance) | [Illuminate\Contracts\Auth\Guard](https://laravel.com/api/9.x/Illuminate/Contracts/Auth/Guard.html) | `auth.driver` +Blade | [Illuminate\View\Compilers\BladeCompiler](https://laravel.com/api/9.x/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` +Broadcast | [Illuminate\Contracts\Broadcasting\Factory](https://laravel.com/api/9.x/Illuminate/Contracts/Broadcasting/Factory.html) |   +Broadcast (Instance) | [Illuminate\Contracts\Broadcasting\Broadcaster](https://laravel.com/api/9.x/Illuminate/Contracts/Broadcasting/Broadcaster.html) |   +Bus | [Illuminate\Contracts\Bus\Dispatcher](https://laravel.com/api/9.x/Illuminate/Contracts/Bus/Dispatcher.html) |   +Cache | [Illuminate\Cache\CacheManager](https://laravel.com/api/9.x/Illuminate/Cache/CacheManager.html) | `cache` +Cache (Instance) | [Illuminate\Cache\Repository](https://laravel.com/api/9.x/Illuminate/Cache/Repository.html) | `cache.store` +Config | [Illuminate\Config\Repository](https://laravel.com/api/9.x/Illuminate/Config/Repository.html) | `config` +Cookie | [Illuminate\Cookie\CookieJar](https://laravel.com/api/9.x/Illuminate/Cookie/CookieJar.html) | `cookie` +Crypt | [Illuminate\Encryption\Encrypter](https://laravel.com/api/9.x/Illuminate/Encryption/Encrypter.html) | `encrypter` +Date | [Illuminate\Support\DateFactory](https://laravel.com/api/9.x/Illuminate/Support/DateFactory.html) | `date` +DB | [Illuminate\Database\DatabaseManager](https://laravel.com/api/9.x/Illuminate/Database/DatabaseManager.html) | `db` +DB (Instance) | [Illuminate\Database\Connection](https://laravel.com/api/9.x/Illuminate/Database/Connection.html) | `db.connection` +Event | [Illuminate\Events\Dispatcher](https://laravel.com/api/9.x/Illuminate/Events/Dispatcher.html) | `events` +File | [Illuminate\Filesystem\Filesystem](https://laravel.com/api/9.x/Illuminate/Filesystem/Filesystem.html) | `files` +Gate | [Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/9.x/Illuminate/Contracts/Auth/Access/Gate.html) |   +Hash | [Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/9.x/Illuminate/Contracts/Hashing/Hasher.html) | `hash` +Http | [Illuminate\Http\Client\Factory](https://laravel.com/api/9.x/Illuminate/Http/Client/Factory.html) |   +Lang | [Illuminate\Translation\Translator](https://laravel.com/api/9.x/Illuminate/Translation/Translator.html) | `translator` +Log | [Illuminate\Log\LogManager](https://laravel.com/api/9.x/Illuminate/Log/LogManager.html) | `log` +Mail | [Illuminate\Mail\Mailer](https://laravel.com/api/9.x/Illuminate/Mail/Mailer.html) | `mailer` +Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/api/9.x/Illuminate/Notifications/ChannelManager.html) |   +Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/9.x/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` +Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/9.x/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` +Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/9.x/Illuminate/Queue/QueueManager.html) | `queue` +Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/9.x/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` +Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/9.x/Illuminate/Queue/Queue.html) |   +Redirect | [Illuminate\Routing\Redirector](https://laravel.com/api/9.x/Illuminate/Routing/Redirector.html) | `redirect` +Redis | [Illuminate\Redis\RedisManager](https://laravel.com/api/9.x/Illuminate/Redis/RedisManager.html) | `redis` +Redis (Instance) | [Illuminate\Redis\Connections\Connection](https://laravel.com/api/9.x/Illuminate/Redis/Connections/Connection.html) | `redis.connection` +Request | [Illuminate\Http\Request](https://laravel.com/api/9.x/Illuminate/Http/Request.html) | `request` +Response | [Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/9.x/Illuminate/Contracts/Routing/ResponseFactory.html) |   +Response (Instance) | [Illuminate\Http\Response](https://laravel.com/api/9.x/Illuminate/Http/Response.html) |   +Route | [Illuminate\Routing\Router](https://laravel.com/api/9.x/Illuminate/Routing/Router.html) | `router` +Schema | [Illuminate\Database\Schema\Builder](https://laravel.com/api/9.x/Illuminate/Database/Schema/Builder.html) |   +Session | [Illuminate\Session\SessionManager](https://laravel.com/api/9.x/Illuminate/Session/SessionManager.html) | `session` +Session (Instance) | [Illuminate\Session\Store](https://laravel.com/api/9.x/Illuminate/Session/Store.html) | `session.store` +Storage | [Illuminate\Filesystem\FilesystemManager](https://laravel.com/api/9.x/Illuminate/Filesystem/FilesystemManager.html) | `filesystem` +Storage (Instance) | [Illuminate\Contracts\Filesystem\Filesystem](https://laravel.com/api/9.x/Illuminate/Contracts/Filesystem/Filesystem.html) | `filesystem.disk` +URL | [Illuminate\Routing\UrlGenerator](https://laravel.com/api/9.x/Illuminate/Routing/UrlGenerator.html) | `url` +Validator | [Illuminate\Validation\Factory](https://laravel.com/api/9.x/Illuminate/Validation/Factory.html) | `validator` +Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/api/9.x/Illuminate/Validation/Validator.html) |   +View | [Illuminate\View\Factory](https://laravel.com/api/9.x/Illuminate/View/Factory.html) | `view` +View (Instance) | [Illuminate\View\View](https://laravel.com/api/9.x/Illuminate/View/View.html) |   +Vite | [Illuminate\Foundation\Vite](https://laravel.com/api/9.x/Illuminate/Foundation/Vite.html) |   diff --git a/docs/filesystem.md b/docs/filesystem.md index 7850f48..0573a4d 100644 --- a/docs/filesystem.md +++ b/docs/filesystem.md @@ -5,6 +5,7 @@ - [Локальный драйвер](#the-local-driver) - [Публичный диск](#the-public-disk) - [Предварительная подготовка драйверов](#driver-prerequisites) + - [Ограниченные файловые системы](#scoped-and-read-only-filesystems) - [Файловые системы, совместимые с Amazon S3](#amazon-s3-compatible-filesystems) - [Доступ к экземплярам дисков](#obtaining-disk-instances) - [Диски по запросу](#on-demand-disks) @@ -34,7 +35,8 @@ Laravel обеспечивает мощную абстракцию файлов Драйвер `local` взаимодействует с файлами, хранящимися локально на сервере, на котором запущено приложение Laravel, в то время как драйвер `s3` используется для записи в службу облачного хранилища Amazon S3. -> {tip} Вы можете настроить столько дисков, сколько захотите, и даже иметь несколько дисков, использующих один и тот же драйвер. +> **Примечание**\ +> Вы можете настроить столько дисков, сколько захотите, и даже иметь несколько дисков, использующих один и тот же драйвер. ### Локальный драйвер @@ -129,7 +131,7 @@ composer require league/flysystem-sftp-v3 "^3.0" // Settings for SSH key based authentication with encryption password... 'privateKey' => env('SFTP_PRIVATE_KEY'), - 'password' => env('SFTP_PASSWORD'), + 'passphrase' => env('SFTP_PASSPHRASE'), // Optional SFTP Settings... // 'hostFingerprint' => env('SFTP_HOST_FINGERPRINT'), @@ -141,15 +143,62 @@ composer require league/flysystem-sftp-v3 "^3.0" // 'useAgent' => true, ], + +### Ограниченные файловые системы + +«Префиксированные» диски позволяют определить файловую систему, в которой все пути автоматически имеют префикс заданного пути. Перед созданием диска с такой файловой системой вам необходимо установить дополнительный пакет Flysystem через менеджер пакетов Composer: + +```shell +composer require league/flysystem-path-prefixing "^3.0" +``` + +Вы можете создать экземпляр любого существующего диска с префиксированной файловой системой, определив диск с использованием драйвера `scoped`. Например, вы можете создать диск, который ограничит ваш существующий диск `s3` определенным префиксом пути, и тогда каждая операция с файлом, использующая такой диск, будет использовать указанный префикс: + +```php +'s3-videos' => [ + 'driver' => 'scoped', + 'disk' => 's3', + 'prefix' => 'path/to/videos', +], +``` + +Диски «только для чтения» позволяют создавать диски с файловой системой, которые не допускают операций записи. Прежде чем использовать параметр конфигурации `read-only`, вам необходимо установить дополнительный пакет Flysystem через менеджер пакетов Composer: + +```shell +composer require league/flysystem-read-only "^3.0" +``` + +Затем вы можете включить параметр конфигурации `read-only` в один или несколько массивов конфигурации ваших дисков: + +```php +'s3-videos' => [ + 'driver' => 's3', + // ... + 'read-only' => true, +], +``` + ### Файловые системы, совместимые с Amazon S3 По умолчанию конфигурационный файл `filesystems` вашего приложения содержит конфигурацию диска `s3`. Помимо использования этого диска для взаимодействия с Amazon S3, вы можете использовать его для взаимодействия с любой совместимой с S3 службой хранения файлов, такой как [MinIO](https://github.com/minio/minio) или [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/). -Обычно после обновления учетных данных диска в соответствии с учетными данными службы, которую вы планируете использовать, вам нужно обновить только значение параметра конфигурации `url`. Значение этого параметра обычно определяется через переменную окружения `AWS_ENDPOINT`: +Обычно после обновления учетных данных диска в соответствии с учетными данными службы, которую вы планируете использовать, вам нужно обновить только значение параметра конфигурации `endpoint`. Значение этого параметра обычно определяется через переменную окружения `AWS_ENDPOINT`: 'endpoint' => env('AWS_ENDPOINT', 'https://minio:9000'), + +#### MinIO + +Чтобы интеграция Laravel Flysystem генерировала правильные URL-адреса при использовании MinIO, вы должны определить переменную среды `AWS_URL`, чтобы она соответствовала локальному URL-адресу вашего приложения и включала имя корзины в путь URL-адреса: + +```ini +AWS_URL=http://localhost:9000/local +``` + +> **Предупреждение**\ +> Генерация URL-адресов временного хранилища с помощью метода `temporaryUrl` не поддерживается при использовании MinIO. + ## Доступ к экземплярам дисков @@ -218,7 +267,8 @@ $disk->put('image.jpg', $content); При использовании драйвера `local` все файлы, которые должны быть общедоступными, должны быть помещены в каталог `storage/app/public`. Кроме того, вы должны [создать символическую ссылку](#the-public-disk) в `public/storage`, которая указывает на каталог `storage/app/public`. -> {note} При использовании драйвера `local` возвращаемое значение `url` не является URL-кодированным. По этой причине мы рекомендуем всегда хранить ваши файлы, используя имена, которые будут создавать допустимые URL-адреса. +> **Предупреждение**\ +> При использовании драйвера `local` возвращаемое значение `url` не является URL-кодированным. По этой причине мы рекомендуем всегда хранить ваши файлы, используя имена, которые будут создавать допустимые URL-адреса. #### Временные URL @@ -420,7 +470,8 @@ $disk->put('image.jpg', $content); 'avatars', $request->file('avatar'), $request->user()->id ); -> {note} Непечатаемые и недопустимые символы Unicode будут автоматически удалены из путей к файлам. По этой причине, вы _по желанию_ можете очистить пути к файлам перед их передачей в методы хранения файлов Laravel. Пути к файлам нормализуются с помощью метода `League\Flysystem\WhitespacePathNormalizer::normalizePath`. +> **Предупреждение**\ +> Непечатаемые и недопустимые символы Unicode будут автоматически удалены из путей к файлам. По этой причине, вы _по желанию_ можете очистить пути к файлам перед их передачей в методы хранения файлов Laravel. Пути к файлам нормализуются с помощью метода `League\Flysystem\WhitespacePathNormalizer::normalizePath`. #### Указание диска diff --git a/docs/fortify.md b/docs/fortify.md index d4d2823..ddefce6 100644 --- a/docs/fortify.md +++ b/docs/fortify.md @@ -32,7 +32,8 @@ Поскольку Fortify не обеспечен собственным пользовательским интерфейсом, он предназначен для работы в паре с вашим собственным пользовательским интерфейсом, который отправляет запросы на регистрируемые им маршруты. Мы обсудим, как именно делать запросы к этим маршрутам, в оставшейся части этой документации. -> {tip} Помните, что Fortify – это пакет, который поможет вам начать реализацию функционала аутентификации Laravel. **Вы не обязаны его использовать.** Вы всегда можете вручную взаимодействовать со службами аутентификации Laravel, следуя документации: [аутентификация](authentication.md), [сброс пароля](passwords.md), и [подтверждение по электронной почте](verification.md). +> **Примечание**\ +> Помните, что Fortify – это пакет, который поможет вам начать реализацию функционала аутентификации Laravel. **Вы не обязаны его использовать.** Вы всегда можете вручную взаимодействовать со службами аутентификации Laravel, следуя документации: [аутентификация](authentication.md), [сброс пароля](passwords.md), и [подтверждение по электронной почте](verification.md). ### Что такое Fortify? @@ -266,7 +267,7 @@ class User extends Authenticatable Затем, вы должны создать экран в своем приложении, где пользователи могут управлять своими настройками двухфакторной аутентификации. Этот экран должен позволить пользователю включать и отключать двухфакторную аутентификацию, а также повторно создавать коды восстановления двухфакторной аутентификации. -> {note} По умолчанию массив `features` конфигурационного файла `config/fortify.php` указывает настройкам двухфакторной аутентификации Fortify требовать подтверждения пароля перед изменением. Поэтому, прежде чем продолжить, ваше приложение должно реализовать функционал [подтверждение пароля](#password-confirmation) Fortify. +> **Предупреждение** > По умолчанию массив `features` конфигурационного файла `config/fortify.php` указывает настройкам двухфакторной аутентификации Fortify требовать подтверждения пароля перед изменением. Поэтому, прежде чем продолжить, ваше приложение должно реализовать функционал [подтверждение пароля](#password-confirmation) Fortify. ### Включение двухфакторной аутентификации @@ -386,7 +387,7 @@ Fortify позаботится об определении маршрута `/re Конечная точка `/register` ожидает строковые поля `name`, `email` / `username`, `password` и `password_confirmation`. Имя поля `email` / `username` должно соответствовать значению конфигурации `username`, определенному в файле конфигурации `fortify` вашего приложения. -Если попытка регистрации будет успешной, то Fortify перенаправит вас на URI, настроенный с помощью параметра конфигурации `home` в файле конфигурации `fortify` вашего приложения. Если запрос на вход был запросом XHR, будет возвращен `200` HTTP-ответ. +Если попытка регистрации будет успешной, то Fortify перенаправит вас на URI, настроенный с помощью параметра конфигурации `home` в файле конфигурации `fortify` вашего приложения. Если запрос был запросом XHR, будет возвращен `201` HTTP-ответ. Если запрос не был успешным, пользователь будет перенаправлен обратно к экрану регистрации, и ошибки валидации будут доступны вам через общедоступную [переменную `$errors` шаблонов Blade](validation.md#quick-displaying-the-validation-errors). Или, в случае запроса XHR, ошибки валидации будут возвращены с `422` HTTP-ответом. diff --git a/docs/frontend.md b/docs/frontend.md new file mode 100644 index 0000000..d74bf20 --- /dev/null +++ b/docs/frontend.md @@ -0,0 +1,197 @@ +# Laravel 9 · Frontend + +- [Introduction](#introduction) +- [Using PHP](#using-php) + - [PHP & Blade](#php-and-blade) + - [Livewire](#livewire) + - [Starter Kits](#php-starter-kits) +- [Using Vue / React](#using-vue-react) + - [Inertia](#inertia) + - [Starter Kits](#inertia-starter-kits) +- [Bundling Assets](#bundling-assets) + + +## Introduction + +Laravel is a backend framework that provides all of the features you need to build modern web applications, such as [routing](/docs/{{version}}/routing), [validation](/docs/{{version}}/validation), [caching](/docs/{{version}}/cache), [queues](/docs/{{version}}/queues), [file storage](/docs/{{version}}/filesystem), and more. However, we believe it's important to offer developers a beautiful full-stack experience, including powerful approaches for building your application's frontend. + +There are two primary ways to tackle frontend development when building an application with Laravel, and which approach you choose is determined by whether you would like to build your frontend by leveraging PHP or by using JavaScript frameworks such as Vue and React. We'll discuss both of these options below so that you can make an informed decision regarding the best approach to frontend development for your application. + + +## Using PHP + + +### PHP & Blade + +In the past, most PHP applications rendered HTML to the browser using simple HTML templates interspersed with PHP `echo` statements which render data that was retrieved from a database during the request: + +```blade +
+ + Hello, name; ?>
+ +
+``` + +In Laravel, this approach to rendering HTML can still be achieved using [views](/docs/{{version}}/views) and [Blade](/docs/{{version}}/blade). Blade is an extremely light-weight templating language that provides convenient, short syntax for displaying data, iterating over data, and more: + +```blade +
+ @foreach ($users as $user) + Hello, {{ $user->name }}
+ @endforeach +
+``` + +When building applications in this fashion, form submissions and other page interactions typically receive an entirely new HTML document from the server and the entire page is re-rendered by the browser. Even today, many applications may be perfectly suited to having their frontends constructed in this way using simple Blade templates. + + +#### Growing Expectations + +However, as user expectations regarding web applications have matured, many developers have found the need to build more dynamic frontends with interactions that feel more polished. In light of this, some developers choose to begin building their application's frontend using JavaScript frameworks such as Vue and React. + +Others, preferring to stick with the backend language they are comfortable with, have developed solutions that allow the construction of modern web application UIs while still primarily utilizing their backend language of choice. For example, in the [Rails](https://rubyonrails.org/) ecosystem, this has spurred the creation of libraries such as [Turbo](https://turbo.hotwired.dev/) [Hotwire](https://hotwired.dev/), and [Stimulus](https://stimulus.hotwired.dev/). + +Within the Laravel ecosystem, the need to create modern, dynamic frontends by primarily using PHP has led to the creation of [Laravel Livewire](https://laravel-livewire.com) and [Alpine.js](https://alpinejs.dev/). + + +### Livewire + +[Laravel Livewire](https://laravel-livewire.com) is a framework for building Laravel powered frontends that feel dynamic, modern, and alive just like frontends built with modern JavaScript frameworks like Vue and React. + +When using Livewire, you will create Livewire "components" that render a discrete portion of your UI and expose methods and data that can be invoked and interacted with from your application's frontend. For example, a simple "Counter" component might look like the following: + +```php +count++; + } + + public function render() + { + return view('livewire.counter'); + } +} +``` + +And, the corresponding template for the counter would be written like so: + +```blade +
+ +

{{ $count }}

+
+``` + +As you can see, Livewire enables you to write new HTML attributes such as `wire:click` that connect your Laravel application's frontend and backend. In addition, you can render your component's current state using simple Blade expressions. + +For many, Livewire has revolutionized frontend development with Laravel, allowing them to stay within the comfort of Laravel while constructing modern, dynamic web applications. Typically, developers using Livewire will also utilize [Alpine.js](https://alpinejs.dev/) to "sprinkle" JavaScript onto their frontend only where it is needed, such as in order to render a dialog window. + +If you're new to Laravel, we recommend getting familiar with the basic usage of [views](/docs/{{version}}/views) and [Blade](/docs/{{version}}/blade). Then, consult the official [Laravel Livewire documentation](https://laravel-livewire.com/docs) to learn how to take your application to the next level with interactive Livewire components. + + +### Starter Kits + +If you would like to build your frontend using PHP and Livewire, you can leverage our Breeze or Jetstream [starter kits](/docs/{{version}}/starter-kits) to jump-start your application's development. Both of these starter kits scaffold your application's backend and frontend authentication flow using [Blade](/docs/{{version}}/blade) and [Tailwind](https://tailwindcss.com) so that you can simply start building your next big idea. + + +## Using Vue / React + +Although it's possible to build modern frontends using Laravel and Livewire, many developers still prefer to leverage the power of a JavaScript framework like Vue or React. This allows developers to take advantage of the rich ecosystem of JavaScript packages and tools available via NPM. + +However, without additional tooling, pairing Laravel with Vue or React would leave us needing to solve a variety of complicated problems such as client-side routing, data hydration, and authentication. Client-side routing is often simplified by using opinionated Vue / React frameworks such as [Nuxt](https://nuxtjs.org/) and [Next](https://nextjs.org/); however, data hydration and authentication remain complicated and cumbersome problems to solve when pairing a backend framework like Laravel with these frontend frameworks. + +In addition, developers are left maintaining two separate code repositories, often needing to coordinate maintenance, releases, and deployments across both repositories. While these problems are not insurmountable, we don't believe it's a productive or enjoyable way to develop applications. + + +### Inertia + +Thankfully, Laravel offers the best of both worlds. [Inertia](https://inertiajs.com) bridges the gap between your Laravel application and your modern Vue or React frontend, allowing you to build full-fledged, modern frontends using Vue or React while leveraging Laravel routes and controllers for routing, data hydration, and authentication — all within a single code repository. With this approach, you can enjoy the full power of both Laravel and Vue / React without crippling the capabilities of either tool. + +After installing Inertia into your Laravel application, you will write routes and controllers like normal. However, instead of returning a Blade template from your controller, you will return an Inertia page: + +```php + User::findOrFail($id) + ]); + } +} +``` + +An Inertia page corresponds to a Vue or React component, typically stored within the `resources/js/Pages` directory of your application. The data given to the page via the `Inertia::render` method will be used to hydrate the "props" of the page component: + +```vue + + + +``` + +As you can see, Inertia allows you to leverage the full power of Vue or React when building your frontend, while providing a light-weight bridge between your Laravel powered backend and your JavaScript powered frontend. + +#### Server-Side Rendering + +If you're concerned about diving into Inertia because your application requires server-side rendering, don't worry. Inertia offers [server-side rendering support](https://inertiajs.com/server-side-rendering). And, when deploying your application via [Laravel Forge](https://forge.laravel.com), it's a breeze to ensure that Inertia's server-side rendering process is always running. + + +### Starter Kits + +If you would like to build your frontend using Inertia and Vue / React, you can leverage our Breeze or Jetstream [starter kits](/docs/{{version}}/starter-kits#breeze-and-inertia) to jump-start your application's development. Both of these starter kits scaffold your application's backend and frontend authentication flow using Inertia, Vue / React, [Tailwind](https://tailwindcss.com), and [Vite](https://vitejs.dev) so that you can start building your next big idea. + + +## Bundling Assets + +Regardless of whether you choose to develop your frontend using Blade and Livewire or Vue / React and Inertia, you will likely need to bundle your application's CSS into production ready assets. Of course, if you choose to build your application's frontend with Vue or React, you will also need to bundle your components into browser ready JavaScript assets. + +By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. Vite provides lightning-fast build times and near instantaneous Hot Module Replacement (HMR) during local development. In all new Laravel applications, including those using our [starter kits](/docs/{{version}}/starter-kits), you will find a `vite.config.js` file that loads our light-weight Laravel Vite plugin that makes Vite a joy to use with Laravel applications. + +The fastest way to get started with Laravel and Vite is by beginning your application's development using [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze), our simplest starter kit that jump-starts your application by providing frontend and backend authentication scaffolding. + +> **Note** +> For more detailed documentation on utilizing Vite with Laravel, please see our [dedicated documentation on bundling and compiling your assets](/docs/{{version}}/vite). diff --git a/docs/hashing.md b/docs/hashing.md index 0fc3c8d..0f5be4e 100644 --- a/docs/hashing.md +++ b/docs/hashing.md @@ -73,7 +73,8 @@ Bcrypt – отличный выбор для хеширования парол 'threads' => 2, ]); -> {tip} Дополнительную информацию об этих параметрах можно найти в [официальной документации PHP](https://www.php.net/manual/ru/function.password-hash.php). +> **Примечание**\ +> Дополнительную информацию об этих параметрах можно найти в [официальной документации PHP](https://www.php.net/manual/ru/function.password-hash.php). ### Проверка совпадения пароля с хешем diff --git a/docs/helpers.md b/docs/helpers.md index 7864874..33cfe91 100644 --- a/docs/helpers.md +++ b/docs/helpers.md @@ -2,6 +2,9 @@ - [Введение](#introduction) - [Доступные методы](#available-methods) +- [Другие утилиты](#other-utilities) + - [Класс Benchmark](#benchmarking) + - [Класс Lottery](#lottery) ## Введение @@ -112,9 +115,11 @@ Laravel содержит множество глобальных «вспомо - [Str::excerpt](#method-excerpt) - [Str::finish](#method-str-finish) - [Str::headline](#method-str-headline) +- [Str::inlineMarkdown](#method-str-inline-markdown) - [Str::is](#method-str-is) - [Str::isAscii](#method-str-is-ascii) - [Str::isJson](#method-str-is-json) +- [Str::isUlid](#method-str-is-ulid) - [Str::isUuid](#method-str-is-uuid) - [Str::kebab](#method-kebab-case) - [Str::lcfirst](#method-str-lcfirst) @@ -152,6 +157,7 @@ Laravel содержит множество глобальных «вспомо - [Str::ucfirst](#method-str-ucfirst) - [Str::ucsplit](#method-str-ucsplit) - [Str::upper](#method-str-upper) +- [Str::ulid](#method-str-ulid) - [Str::uuid](#method-str-uuid) - [Str::wordCount](#method-str-word-count) - [Str::words](#method-str-words) @@ -185,11 +191,14 @@ Laravel содержит множество глобальных «вспомо - [exactly](#method-fluent-str-exactly) - [explode](#method-fluent-str-explode) - [finish](#method-fluent-str-finish) +- [headline](#method-fluent-str-headline) +- [inlineMarkdown](#method-fluent-str-inline-markdown) - [is](#method-fluent-str-is) - [isAscii](#method-fluent-str-is-ascii) - [isEmpty](#method-fluent-str-is-empty) - [isNotEmpty](#method-fluent-str-is-not-empty) - [isJson](#method-fluent-str-is-json) +- [isUlid](#method-fluent-str-is-ulid) - [isUuid](#method-fluent-str-is-uuid) - [kebab](#method-fluent-str-kebab) - [lcfirst](#method-fluent-str-lcfirst) @@ -242,8 +251,10 @@ Laravel содержит множество глобальных «вспомо - [whenStartsWith](#method-fluent-str-when-starts-with) - [whenEndsWith](#method-fluent-str-when-ends-with) - [whenExactly](#method-fluent-str-when-exactly) +- [whenNotExactly](#method-fluent-str-when-not-exactly) - [whenIs](#method-fluent-str-when-is) - [whenIsAscii](#method-fluent-str-when-is-ascii) +- [whenIsUlid](#method-fluent-str-when-is-ulid) - [whenIsUuid](#method-fluent-str-when-is-uuid) - [whenTest](#method-fluent-str-when-test) - [wordCount](#method-fluent-str-word-count) @@ -294,6 +305,7 @@ Laravel содержит множество глобальных «вспомо - [encrypt](#method-encrypt) - [env](#method-env) - [event](#method-event) +- [fake](#method-fake) - [filled](#method-filled) - [info](#method-info) - [logger](#method-logger) @@ -304,6 +316,8 @@ Laravel содержит множество глобальных «вспомо - [policy](#method-policy) - [redirect](#method-redirect) - [report](#method-report) +- [report_if](#method-report-if) +- [report_unless](#method-report-unless) - [request](#method-request) - [rescue](#method-rescue) - [resolve](#method-resolve) @@ -607,11 +621,11 @@ Laravel содержит множество глобальных «вспомо use Illuminate\Support\Arr; - $isAssoc = Arr::isList(['foo', 'bar', 'baz']); + $isList = Arr::isList(['foo', 'bar', 'baz']); // true - $isAssoc = Arr::isList(['product' => ['name' => 'Desk', 'price' => 100]]); + $isList = Arr::isList(['product' => ['name' => 'Desk', 'price' => 100]]); // false @@ -1096,7 +1110,7 @@ Laravel содержит множество глобальных «вспомо $data = ['products' => ['desk' => ['price' => 100]]]; - data_set($data, 'products.desk.price', 200, $overwrite = false); + data_set($data, 'products.desk.price', 200, overwrite: false); // ['products' => ['desk' => ['price' => 100]]] @@ -1445,6 +1459,17 @@ Laravel содержит множество глобальных «вспомо // Email Notification Sent + +#### `Str::inlineMarkdown()` + +Метод `Str::inlineMarkdown` конвертирует текст с разметкой [GitHub flavored Markdown](https://github.github.com/gfm/) в строку HTML с помощью [CommonMark](https://commonmark.thephpleague.com/). Однако, в отличие от метода [`markdown`](#method-str-markdown), он не заключает весь сгенерированный HTML в блочный элемент: + + use Illuminate\Support\Str; + + $html = Str::inlineMarkdown('**Laravel**'); + + // Laravel + #### `Str::is()` @@ -1494,6 +1519,21 @@ Laravel содержит множество глобальных «вспомо // false + +#### `Str::isUlid()` + +Метод `Str::isUlid` определяет, является ли переданная строка допустимым ULID: + + use Illuminate\Support\Str; + + $isUlid = Str::isUlid('01gd6r360bp37zj17nxb55yv40'); + + // true + + $isUlid = Str::isUlid('laravel'); + + // false + #### `Str::isUuid()` @@ -1995,6 +2035,17 @@ Laravel содержит множество глобальных «вспомо // LARAVEL + +#### `Str::ulid()` + +Метод `Str::ulid` генерирует ULID: + + use Illuminate\Support\Str; + + return (string) Str::ulid(); + + // 01gd6r360bp37zj17nxb55yv40 + #### `Str::uuid()` @@ -2328,6 +2379,32 @@ Str::wordCount('Hello, world!'); // 2 // this/string/ + +#### `headline` + +The `headline` method will convert strings delimited by casing, hyphens, or underscores into a space delimited string with each word's first letter capitalized: + + use Illuminate\Support\Str; + + $headline = Str::of('taylor_otwell')->headline(); + + // Taylor Otwell + + $headline = Str::of('EmailNotificationSent')->headline(); + + // Email Notification Sent + + +#### `inlineMarkdown` + +Метод `inlineMarkdown` конвертирует текст с разметкой [GitHub flavored Markdown](https://github.github.com/gfm/) в строку HTML с помощью [CommonMark](https://commonmark.thephpleague.com/). Однако, в отличие от метода [`markdown`](#method-fluent-str-markdown), он не заключает весь сгенерированный HTML в блочный элемент: + + use Illuminate\Support\Str; + + $html = Str::of('**Laravel**')->inlineMarkdown(); + + // Laravel + #### `is` @@ -2408,6 +2485,21 @@ Str::wordCount('Hello, world!'); // 2 // false + +#### `isUlid` + +Метод `isUlid` определяет, является ли переданная строка допустимым ULID: + + use Illuminate\Support\Str; + + $result = Str::of('01gd6r360bp37zj17nxb55yv40')->isUlid(); + + // true + + $result = Str::of('Taylor')->isUlid(); + + // false + #### `isUuid` @@ -2941,7 +3033,7 @@ Str::wordCount('Hello, world!'); // 2 $string = Str::of('Laravel') ->append(' Framework') ->tap(function ($string) { - dump('String after append: ' . $string); + dump('String after append: '.$string); }) ->upper(); @@ -3141,6 +3233,19 @@ Str::wordCount('Hello, world!'); // 2 // 'Laravel' + +#### `whenNotExactly` + +Метод `whenNotExactly` вызывает переданное замыкание, если строка имеет не точное соответствие указанной строке. Замыкание получит экземпляр Fluent: + + use Illuminate\Support\Str; + + $string = Str::of('framework')->whenNotExactly('laravel', function ($string) { + return $string->title(); + }); + + // 'Framework' + #### `whenIs` @@ -3161,12 +3266,25 @@ Str::wordCount('Hello, world!'); // 2 use Illuminate\Support\Str; - $string = Str::of('foo/bar')->whenIsAscii('laravel', function ($string) { + $string = Str::of('laravel')->whenIsAscii(function ($string) { return $string->title(); }); // 'Laravel' + +#### `whenIsUlid` + +Метод `whenIsUlid` вызывает переданное замыкание, если строка является допустимым ULID. Замыкание получит экземпляр Fluent: + + use Illuminate\Support\Str; + + $string = Str::of('01gd6r360bp37zj17nxb55yv40')->whenIsUlid(function ($string) { + return $string->substr(0, 8); + }); + + // '01gd6r36' + #### `whenIsUuid` @@ -3174,7 +3292,7 @@ Str::wordCount('Hello, world!'); // 2 use Illuminate\Support\Str; - $string = Str::of('foo/bar')->whenIsUuid('a0a2a2d2-0b87-4a18-83f2-2529882be2de', function ($string) { + $string = Str::of('a0a2a2d2-0b87-4a18-83f2-2529882be2de')->whenIsUuid(function ($string) { return $string->substr(0, 8); }); @@ -3517,7 +3635,8 @@ Str::of('Hello, world!')->wordCount(); // 2 $env = env('APP_ENV', 'production'); -> {note} Если вы выполнили команду `config:cache` во время процесса развертывания, вы должны быть уверены, что вызываете функцию `env` только из файлов конфигурации. Как только конфигурации будут кешированы, файл `.env` не будет загружаться, и все вызовы функции `env` будут возвращать `null`. +> **Предупреждение**\ +> Если вы выполнили команду `config:cache` во время процесса развертывания, вы должны быть уверены, что вызываете функцию `env` только из файлов конфигурации. Как только конфигурации будут кешированы, файл `.env` не будет загружаться, и все вызовы функции `env` будут возвращать `null`. #### `event()` @@ -3526,6 +3645,27 @@ Str::of('Hello, world!')->wordCount(); // 2 event(new UserRegistered($user)); + +#### `fake()` + +Функция `fake` извлекает из контейнера синглтон [Faker](https://github.com/FakerPHP/Faker), что может быть полезно при создании фиктивных данных в фабриках моделей, заполнении баз данных, тестах и прототипировании шаблонов: + +```blade +@for($i = 0; $i < 10; $i++) +
+
Name
+
{{ fake()->name() }}
+ +
Email
+
{{ fake()->unique()->safeEmail() }}
+
+@endfor +``` + +По умолчанию функция `fake` будет использовать параметр конфигурации `faker_locale` вашего конфигурационного файла `config/app.php`; вы также можете указать языковой стандарт, передав его функции `fake`. Каждому языковому стандарту соответствует свой синглтон: + + fake('nl_NL')->name() + #### `filled()` @@ -3649,6 +3789,24 @@ Str::of('Hello, world!')->wordCount(); // 2 report('Something went wrong.'); + +#### `report_if()` + +Функция `report_if` сообщит об исключении, используя ваш [обработчик исключений](errors.md#the-exception-handler), если переданное условие истинно: + + report_if($shouldReport, $e); + + report_if($shouldReport, 'Something went wrong.'); + + +#### `report_unless()` + +Функция `report_unless` сообщит об исключении, используя ваш [обработчик исключений](errors.md#the-exception-handler), если переданное условие не является истинным: + + report_unless($reportingDisabled, $e); + + report_unless($reportingDisabled, 'Something went wrong.'); + #### `request()` @@ -3714,7 +3872,7 @@ Str::of('Hello, world!')->wordCount(); // 2 Для удобства вы можете предоставить массив в качестве первого аргумента функции `retry`. Этот массив будет использоваться для определения количества миллисекунд ожидания между последующими попытками: - return retry([100, 200] function () { + return retry([100, 200], function () { // Ждем 100 мс при первой попытке, 200 мс при второй попытке ... }); @@ -3875,3 +4033,69 @@ Str::of('Hello, world!')->wordCount(); // 2 $result = with(5, null); // 5 + + +## Другие утилиты + + +### Класс Benchmark + +Иногда требуется быстро протестировать производительность определенных частей вашего приложения. В таких случаях вы можете использовать класс `Benchmark` для измерения количества миллисекунд, которое требуется для выполнения переданных замыканий: + + User::find(1)); // 0.1 ms + + Benchmark::dd([ + 'Scenario 1' => fn () => User::count(), // 0.5 ms + 'Scenario 2' => fn () => User::all()->count(), // 20.0 ms + ]); + +По умолчанию переданные замыкания будут выполняться один раз (одна итерация), а их продолжительность будет отображаться в браузере или консоли. + +Чтобы вызвать замыкание более одного раза, вы можете указать количество итераций в качестве второго аргумента метода. При выполнении замыкания более одного раза класс `Benchmark` вернет среднее количество миллисекунд, которое потребовалось для выполнения замыкания во всех итерациях: + + Benchmark::dd(fn () => User::count(), iterations: 10); // 0.5 ms + + +### Класс Lottery + +Класс Laravel Lottery может использоваться для выполнения замыканий, основываясь на выборе целого числа из переданных в замкнутом интервале. Это может быть особенно полезно, когда вы хотите выполнять код только для процента ваших входящих запросов: + + use Illuminate\Support\Lottery; + + Lottery::odds(1, 20) + ->winner(fn () => $user->won()) + ->loser(fn () => $user->lost()) + ->choose(); + +Вы можете комбинировать класс Laravel Lottery с другими функциями. Например, вы можете сообщить обработчику исключений только о небольшом проценте медленных запросов. И, поскольку класс Lottery является вызываемым, мы можем передать экземпляр класса в любой метод, который принимает вызываемые объекты: + + use Carbon\CarbonInterval; + use Illuminate\Support\Facades\DB; + use Illuminate\Support\Lottery; + + DB::whenQueryingForLongerThan( + CarbonInterval::seconds(2), + Lottery::odds(1, 100)->winner(fn () => report('Querying > 2 seconds.')), + ); + + +#### Тестирование Lottery + +Laravel предоставляет несколько простых методов, позволяющих легко тестировать вызовы Lottery вашего приложения: + + // Всегда выигрышная ситуация ... + Lottery::alwaysWin(); + + // Всегда проигрышная ситуация ... + Lottery::alwaysLose(); + + // Сначала выигрышная ситуация, затем проигрышная и далее обычное поведение ... + Lottery::fix([true, false]); + + // Возврат к обычному поведению ... + Lottery::determineResultNormally(); diff --git a/docs/homestead.md b/docs/homestead.md index 3d793a7..37d951a 100644 --- a/docs/homestead.md +++ b/docs/homestead.md @@ -42,7 +42,8 @@ Laravel стремится сделать весь процесс разрабо Homestead работает в любой системе: Windows, macOS или Linux и содержит Nginx, PHP, MySQL, PostgreSQL, Redis, Memcached, Node, а также другое программное обеспечение, необходимое для разработки потрясающих приложений Laravel. -> {note} Если вы используете Windows, то вам может потребоваться включить аппаратную виртуализацию (VT-x). Обычно это можно выполнить в BIOS. Если вы используете Hyper-V в системе UEFI, то вам может дополнительно потребоваться отключить Hyper-V, чтобы получить доступ к VT-x. +> **Предупреждение**\ +> Если вы используете Windows, то вам может потребоваться включить аппаратную виртуализацию (VT-x). Обычно это можно выполнить в BIOS. Если вы используете Hyper-V в системе UEFI, то вам может дополнительно потребоваться отключить Hyper-V, чтобы получить доступ к VT-x. ### Прилагаемое программное обеспечение @@ -59,6 +60,7 @@ Homestead работает в любой системе: Windows, macOS или L - Ubuntu 20.04 - Git +- PHP 8.2 - PHP 8.1 - PHP 8.0 - PHP 7.4 @@ -71,8 +73,9 @@ Homestead работает в любой системе: Windows, macOS или L - MySQL 8.0 - lmm - Sqlite3 -- PostgreSQL 13 +- PostgreSQL 15 - Composer +- Docker - Node (включая Yarn, Bower, Grunt и Gulp) - Redis - Memcached @@ -105,7 +108,6 @@ Homestead работает в любой системе: Windows, macOS или L - Chronograf - CouchDB - Фреймворк Lucky (Crystal) -- Docker - Elasticsearch - EventStoreDB - Gearman @@ -183,7 +185,8 @@ init.bat provider: virtualbox -> {note} Если вы используете Apple Silicon, то вам следует добавить `box: laravel/homestead-arm` в ваш файл `Homestead.yaml`. Для Apple Silicon требуется провайдер Parallels. +> **Предупреждение**\ +> Если вы используете Apple Silicon, то вам следует добавить `box: laravel/homestead-arm` в ваш файл `Homestead.yaml`. Для Apple Silicon требуется провайдер Parallels. #### Конфигурирование общих папок @@ -196,7 +199,8 @@ folders: to: /home/vagrant/project1 ``` -> {note} Пользователи Windows не должны использовать синтаксис пути `~/`, вместо этого необходимо использовать полный путь к своему проекту, например, `C:\Users\user\Code\project1`. +> **Предупреждение**\ +> Пользователи Windows не должны использовать синтаксис пути `~/`, вместо этого необходимо использовать полный путь к своему проекту, например, `C:\Users\user\Code\project1`. Вы всегда должны индивидуально сопоставлять каждое приложение с собственной папкой приложения вместо сопоставления одного большого каталога, содержащего все ваши приложения. При сопоставлении папки виртуальная машина должна отслеживать все операции ввода-вывода диска для *каждого* файла в папке. При наличии множества файлов в папке, может возникнуть снижение производительности: @@ -208,7 +212,8 @@ folders: to: /home/vagrant/project2 ``` -> {note} Вы никогда не должны монтировать текущий каталог `.` при использовании Homestead. Это приводит к тому, что Vagrant не отображает текущую папку в `/vagrant`, что нарушает работу дополнительных функций и приводит к неожиданным результатам во время подготовки. +> **Предупреждение**\ +> Вы никогда не должны монтировать текущий каталог `.` при использовании Homestead. Это приводит к тому, что Vagrant не отображает текущую папку в `/vagrant`, что нарушает работу дополнительных функций и приводит к неожиданным результатам во время подготовки. Чтобы задействовать [NFS](https://www.vagrantup.com/docs/synced-folders/nfs.html), вы можете добавить параметр `type` при сопоставлении папок: @@ -219,7 +224,8 @@ folders: type: "nfs" ``` -> {note} При использовании NFS в Windows вам следует рассмотреть возможность установки плагина [vagrant-winnfsd](https://github.com/winnfsd/vagrant-winnfsd). Этот плагин будет поддерживать корректные разрешения пользователя / группы для файлов и каталогов виртуальной машины Homestead. +> **Предупреждение**\ +> При использовании NFS в Windows вам следует рассмотреть возможность установки плагина [vagrant-winnfsd](https://github.com/winnfsd/vagrant-winnfsd). Этот плагин будет поддерживать корректные разрешения пользователя / группы для файлов и каталогов виртуальной машины Homestead. Вы также можете передать любые параметры, поддерживаемые [синхронизируемыми папками](https://www.vagrantup.com/docs/synced-folders/basic_usage.html) Vagrant, указав их под параметром `options`: @@ -246,7 +252,8 @@ sites: Если вы измените параметр `sites` после подготовки виртуальной машины Homestead, то вы должны выполнить команду `vagrant reload --provision` в своем терминале, чтобы обновить конфигурацию Nginx на виртуальной машине. -> {note} Скрипты Homestead созданы максимально идемпотентными. Однако, если у вас возникли проблемы во время подготовки, то вам следует уничтожить и пересоздать машину, выполнив команду `vagrant destroy && vagrant up`. +> **Предупреждение**\ +> Скрипты Homestead созданы максимально идемпотентными. Однако, если у вас возникли проблемы во время подготовки, то вам следует уничтожить и пересоздать машину, выполнив команду `vagrant destroy && vagrant up`. #### Разрешение имени хоста @@ -324,7 +331,6 @@ features: - chronograf: true - couchdb: true - crystal: true - - docker: true - elasticsearch: version: 7.9.0 - eventstore: true @@ -337,6 +343,7 @@ features: - meilisearch: true - minio: true - mongodb: true + - mysql: true - neo4j: true - ohmyzsh: true - openresty: true @@ -356,7 +363,8 @@ features: Вы можете указать поддерживаемую версию Elasticsearch, которая должна быть точным номером версии в формате `major.minor.patch`. При установке по умолчанию будет создан кластер с именем `homestead`. Вы никогда не должны выделять для Elasticsearch более половины памяти операционной системы, поэтому убедитесь, что на вашей виртуальной машине Homestead выделено как минимум вдвое больше памяти Elasticsearch. -> {tip} Ознакомьтесь с [документацией Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current), чтобы узнать, как настроить свою конфигурацию. +> **Примечание**\ +> Ознакомьтесь с [документацией Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current), чтобы узнать, как настроить свою конфигурацию. #### MariaDB @@ -453,7 +461,8 @@ sites: to: /home/vagrant/project2/public ``` -> {note} Перед добавлением сайта убедитесь, что вы настроили [сопоставление папок](#configuring-shared-folders) для каталога проекта. +> **Предупреждение**\ +> Перед добавлением сайта убедитесь, что вы настроили [сопоставление папок](#configuring-shared-folders) для каталога проекта. Если Vagrant не управляет вашим файлом `hosts` автоматически, то вам может потребоваться также добавить новый сайт в этот файл. В macOS и Linux этот файл находится в `/etc/hosts`. В Windows он находится в `C:\Windows\System32\drivers\etc\hosts`: @@ -548,7 +557,7 @@ ports: ### Версии PHP -В Homestead 6 появилась поддержка запуска нескольких версий PHP на одной виртуальной машине. Вы можете указать, какую версию PHP использовать для конкретного сайта в файле `Homestead.yaml`. Доступные версии PHP: «5.6», «7.0», «7.1», «7.2», «7.3», «7.4», «8.0» (по умолчанию) и «8.1»: +Homestead поддерживает запуск нескольких версий PHP на одной виртуальной машине. Вы можете указать, какую версию PHP использовать для конкретного сайта в файле `Homestead.yaml`. Доступные версии PHP: «5.6», «7.0», «7.1», «7.2», «7.3», «7.4», «8.0», «8.1» и «8.2» (по умолчанию): ```yaml sites: @@ -568,6 +577,7 @@ php7.3 artisan list php7.4 artisan list php8.0 artisan list php8.1 artisan list +php8.2 artisan list ``` Вы можете изменить версию PHP, используемую по умолчанию в CLI, выполнив следующие команды на своей виртуальной машине Homestead: @@ -581,6 +591,7 @@ php73 php74 php80 php81 +php82 ``` @@ -588,7 +599,8 @@ php81 База данных `homestead` настроена из коробки как для MySQL, так и для PostgreSQL. Чтобы подключиться к вашей базе данных MySQL или PostgreSQL из клиента базы данных вашего хост-компьютера, вы должны подключиться к `127.0.0.1` через порт `33060` (MySQL) или `54320` (PostgreSQL). Имя пользователя и пароль для обеих баз данных – `homestead` / `secret`. -> {note} Вы должны использовать эти нестандартные порты только при подключении к базам данных с вашего хост-компьютера. Вы будете использовать по умолчанию порты `3306` и `5432` в конфигурационном файле `database` вашего приложения Laravel, поскольку Laravel работает _внутри_ виртуальной машины. +> **Предупреждение**\ +> Вы должны использовать эти нестандартные порты только при подключении к базам данных с вашего хост-компьютера. Вы будете использовать по умолчанию порты `3306` и `5432` в конфигурационном файле `database` вашего приложения Laravel, поскольку Laravel работает _внутри_ виртуальной машины. ### Резервное копирование базы данных @@ -702,7 +714,8 @@ share homestead.test share homestead.test -region=eu -subdomain=laravel ``` -> {note} Помните, что Vagrant по своей сути небезопасен, и вы открываете свою виртуальную машину для доступа в Интернет, выполняя команду `share`. +> **Предупреждение**\ +> Помните, что Vagrant по своей сути небезопасен, и вы открываете свою виртуальную машину для доступа в Интернет, выполняя команду `share`. ## Отладка и профилирование @@ -714,7 +727,8 @@ Homestead содержит поддержку пошаговой отладки По умолчанию Xdebug уже запущен и готов принимать соединения. Если вам нужно задействовать Xdebug в CLI, выполните команду `sudo phpenmod xdebug` на вашей виртуальной машине Homestead. Затем следуйте инструкциям вашей IDE, чтобы включить отладку. Наконец, настройте свой браузер для запуска Xdebug с расширением или [букмарклетом](https://www.jetbrains.com/phpstorm/marklets/). -> {note} Xdebug заставляет PHP работать значительно медленнее. Чтобы отключить Xdebug, запустите `sudo phpdismod xdebug` на виртуальной машине Homestead и перезапустите службу FPM. +> **Предупреждение**\ +> Xdebug заставляет PHP работать значительно медленнее. Чтобы отключить Xdebug, запустите `sudo phpdismod xdebug` на виртуальной машине Homestead и перезапустите службу FPM. #### Автозапуск Xdebug @@ -723,8 +737,9 @@ Homestead содержит поддержку пошаговой отладки ```ini ; Если Homestead.yaml содержит другую подсеть для IP-адреса, этот адрес может быть другим ... -xdebug.remote_host = 192.168.10.1 -xdebug.remote_autostart = 1 +xdebug.client_host = 192.168.10.1 +xdebug.mode = debug +xdebug.start_with_request = yes ``` @@ -752,7 +767,7 @@ features: client_token: "client_value" ``` -Учетные данные сервера и клиента [требуют учетной записи Blackfire](https://blackfire.io/signup). Blackfire предлагает различные варианты профилирования приложения, включая инструмент командной строки и расширение браузера. Пожалуйста, [просмотрите документацию Blackfire](https://blackfire.io/docs/cookbooks/index) для получения дополнительной информации. +Учетные данные сервера и клиента [требуют учетной записи Blackfire](https://blackfire.io/signup). Blackfire предлагает различные варианты профилирования приложения, включая инструмент командной строки и расширение браузера. Пожалуйста, [просмотрите документацию Blackfire](https://blackfire.io/docs/php/integrations/laravel/index) для получения дополнительной информации. ## Сетевые интерфейсы diff --git a/docs/horizon.md b/docs/horizon.md index a834807..933c1e1 100644 --- a/docs/horizon.md +++ b/docs/horizon.md @@ -5,6 +5,7 @@ - [Конфигурирование](#configuration) - [Стратегии балансировки](#balancing-strategies) - [Авторизация доступа к панели управления](#dashboard-authorization) + - [Неотслеживаемые задания](#silenced-jobs) - [Обновление Horizon](#upgrading-horizon) - [Запуск Horizon](#running-horizon) - [Развертывание Horizon](#deploying-horizon) @@ -17,7 +18,8 @@ ## Введение -> {tip} Прежде чем углубиться в Laravel Horizon, вам следует ознакомиться с базовыми [службами очередей](queues.md) Laravel. Horizon дополняет очередь Laravel дополнительным функционалом, которые могут сбивать с толку, если вы еще не знакомы с основным функционалом очереди, предлагаемыми Laravel. +> **Примечание**\ +> Прежде чем углубиться в Laravel Horizon, вам следует ознакомиться с базовыми [службами очередей](queues.md) Laravel. Horizon дополняет очередь Laravel дополнительным функционалом, которые могут сбивать с толку, если вы еще не знакомы с основным функционалом очереди, предлагаемыми Laravel. [Laravel Horizon](https://github.com/laravel/horizon) предлагает красивую панель управления и конфигурацию на основе кода для ваших [очередей Redis](queues.md), работающих на Laravel. Horizon позволяет легко отслеживать ключевые показатели вашей системы очередей, такие как пропускная способность, время выполнения и сбои заданий. @@ -28,7 +30,8 @@ ## Установка -> {note} Laravel Horizon требует, чтобы вы использовали [Redis](https://redis.io) для управления вашей очередью. Следовательно, вы должны убедиться, что ваше соединение с очередью установлено на `redis` в конфигурационном файле `config/queue.php` вашего приложения. +> **Предупреждение**\ +> Laravel Horizon требует, чтобы вы использовали [Redis](https://redis.io) для управления вашей очередью. Следовательно, вы должны убедиться, что ваше соединение с очередью установлено на `redis` в конфигурационном файле `config/queue.php` вашего приложения. Для начала установите Horizon с помощью менеджера пакетов Composer в свой проект: @@ -47,7 +50,8 @@ php artisan horizon:install После публикации ресурсов Horizon его основной конфигурационный файл будет расположен по адресу `config/horizon.php`. Этот конфигурационный файл позволяет вам настроить параметры обработчика очереди для вашего приложения. Каждый параметр конфигурации включает описание своего назначения, поэтому обязательно внимательно изучите этот файл. -> {note} Horizon внутренне использует соединение Redis с именем `horizon`. Данное имя соединения Redis зарезервировано и не должно назначаться другому соединению Redis в конфигурационном файле `database.php` или в качестве значения параметра `use` в конфигурационном файле `horizon.php`. +> **Предупреждение**\ +> Horizon внутренне использует соединение Redis с именем `horizon`. Данное имя соединения Redis зарезервировано и не должно назначаться другому соединению Redis в конфигурационном файле `database.php` или в качестве значения параметра `use` в конфигурационном файле `horizon.php`. #### Окружение @@ -72,12 +76,13 @@ php artisan horizon:install Когда вы запускаете Horizon, он будет использовать параметры конфигурации рабочего процесса для окружения, в котором работает ваше приложение. Обычно окружение определяется значением [переменной окружения](configuration.md#determining-the-current-environment) `APP_ENV`. Например, окружение `local` Horizon по умолчанию настроено на запуск трех рабочих процессов и автоматический баланс количества рабочих процессов, назначенных каждой очереди. Окружение `production` по умолчанию настроено на запуск максимум 10 рабочих процессов и автоматический баланс количества рабочих процессов, назначенных каждой очереди. -> {note} Вы должны убедиться, что часть `environments` вашего конфигурационного файла `config/horizon.php` содержит запись для каждого [окружения](configuration.md#environment-configuration), на котором вы планируете запускать Horizon. +> **Предупреждение**\ +> Вы должны убедиться, что часть `environments` вашего конфигурационного файла `config/horizon.php` содержит запись для каждого [окружения](configuration.md#environment-configuration), на котором вы планируете запускать Horizon. #### Supervisors -Как вы можете видеть в конфигурационном файле Horizon по умолчанию каждое окружение может содержать один или более диспетчеров процессов. По умолчанию в конфигурационном файле этот диспетчер определяется как `supervisor-1`; однако вы можете называть своих диспетчеров процессов как хотите. Каждый диспетчер процессов, по сути, отвечает за «наблюдение» за группой рабочих процессов и заботится о балансировке рабочих процессов по очередям. +Как вы можете видеть в конфигурационном файле Horizon (по умолчанию), каждое окружение может содержать один или более диспетчеров процессов. По умолчанию в конфигурационном файле этот диспетчер определяется как `supervisor-1`; однако вы можете называть своих диспетчеров процессов как хотите. Каждый диспетчер процессов, по сути, отвечает за «наблюдение» за группой рабочих процессов и заботится о балансировке рабочих процессов по очередям. Вы можете добавить дополнительных диспетчеров процессов в конкретное окружение, если хотите определить новую группу рабочих процессов, которые должны выполняться в этом окружении. Вы можете сделать это, если хотите определить другую стратегию балансировки или количество рабочих процессов для конкретной очереди, используемой вашим приложением. @@ -112,9 +117,9 @@ php artisan horizon:install ], ], -Значения конфигурации `balanceMaxShift` и `balanceCooldown` определяют, насколько быстро Horizon будет масштабироваться в соответствии с требованиями обработчиков. В приведенном выше примере каждые три секунды будет создаваться или уничтожаться максимум один новый процесс. Вы можете изменять эти значения по мере необходимости в зависимости от потребностей вашего приложения. +Значения конфигурации `balanceMaxShift` и `balanceCooldown` определяют то, насколько быстро Horizon будет масштабироваться в соответствии с требованиями обработчиков. В приведенном выше примере каждые три секунды будет создаваться или уничтожаться максимум один новый процесс. Вы можете изменять эти значения по мере необходимости в зависимости от потребностей вашего приложения. -Когда для параметра `balance` установлено значение `false`, то будет использоваться поведение Laravel по умолчанию, которое обрабатывает очереди в том порядке, в котором они перечислены в вашей конфигурации. +Когда для параметра `balance` установлено значение `false`, то будет использоваться поведение Laravel по умолчанию, при котором очереди обрабатываются в том порядке, в котором они перечислены в вашей конфигурации. ### Авторизация доступа к панели управления @@ -142,6 +147,26 @@ php artisan horizon:install Помните, что Laravel автоматически внедрит аутентифицированного пользователя в замыкание шлюза авторизации. Если ваше приложение обеспечивает безопасность Horizon с помощью другого метода, например через ограничения по IP-адресам, то пользователям Horizon может не потребоваться «вход в систему». Следовательно, вам нужно будет изменить сигнатуру замыкания `function ($user)`, указанную выше, на `function ($user = null)`, чтобы указать Laravel не требовать аутентификации. + +### Неотслеживаемые задания + +Иногда не требуется просматривать определенные задания, отправленные вашим приложением или сторонними пакетами. Вместо того, чтобы эти задания занимали место в вашем списке «Completed Jobs», вы можете отключить их. Для начала добавьте имя класса задания в параметр конфигурации `silenced` в конфигурационном файле `horizon` вашего приложения: + + 'silenced' => [ + App\Jobs\ProcessPodcast::class, + ], + +Кроме того, задание, которое вы хотите отключить, может реализовать интерфейс `Laravel\Horizon\Contracts\Silenced`. Если задание реализует этот интерфейс, то оно будет автоматически отключено, даже если его нет в массиве конфигурации `silenced`: + + use Laravel\Horizon\Contracts\Silenced; + + class ProcessPodcast implements ShouldQueue, Silenced + { + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + // ... + } + ## Обновление Horizon @@ -220,7 +245,8 @@ Supervisor – это монитор процесса для операцион sudo apt-get install supervisor ``` -> {tip} Если настройка Supervisor сама по себе кажется утомительной, рассмотрите возможность использования [Laravel Forge](https://forge.laravel.com), который автоматически установит и настроит Supervisor для ваших проектов Laravel. +> **Примечание**\ +> Если настройка Supervisor сама по себе кажется утомительной, рассмотрите возможность использования [Laravel Forge](https://forge.laravel.com), который автоматически установит и настроит Supervisor для ваших проектов Laravel. #### Конфигурирование Supervisor @@ -241,7 +267,8 @@ stopwaitsecs=3600 При определении конфигурации Supervisor вы должны убедиться, что значение `stopwaitsecs` больше, чем количество секунд, затраченных вашим самым продолжительным заданием. В противном случае Supervisor может убить задание до завершения его обработки. -> {note} Хотя приведенные выше примеры действительны для серверов на базе Ubuntu, расположение и расширение файла, ожидаемые от файлов конфигурации Supervisor, могут различаться в разных серверных операционных системах. Пожалуйста, обратитесь к документации вашего сервера для получения дополнительной информации. +> **Предупреждение**\ +> Хотя приведенные выше примеры действительны для серверов на базе Ubuntu, расположение и расширение файла, ожидаемые от файлов конфигурации Supervisor, могут различаться в разных серверных операционных системах. Пожалуйста, обратитесь к документации вашего сервера для получения дополнительной информации. #### Запуск Supervisor @@ -256,7 +283,8 @@ sudo supervisorctl update sudo supervisorctl start horizon ``` -> {tip} Для получения дополнительной информации о запуске Supervisor обратитесь к [документации Supervisor](http://supervisord.org/index.html). +> **Примечание**\ +> Для получения дополнительной информации о запуске Supervisor обратитесь к [документации Supervisor](http://supervisord.org/index.html). ## Метки @@ -337,7 +365,8 @@ Horizon позволяет назначать «метки» заданиям, ## Уведомления -> {note} При настройке Horizon для отправки уведомлений Slack или SMS необходимо ознакомиться с [предварительной подготовкой для соответствующего канала уведомлений](notifications.md). +> **Предупреждение**\ +> При настройке Horizon для отправки уведомлений Slack или SMS необходимо ознакомиться с [предварительной подготовкой для соответствующего канала уведомлений](notifications.md). Если вы хотите получать уведомления, когда одна из ваших очередей имеет длительное время ожидания, то вы можете использовать методы `Horizon::routeMailNotificationsTo`, `Horizon::routeSlackNotificationsTo` и `Horizon::routeSmsNotificationsTo`. Как правило, вызов этих методов осуществляется в методе `boot` поставщика `App\Providers\HorizonServiceProvider`: diff --git a/docs/http-client.md b/docs/http-client.md index b657b25..797f1d4 100644 --- a/docs/http-client.md +++ b/docs/http-client.md @@ -8,6 +8,7 @@ - [Время ожидания](#timeout) - [Повторные попытки](#retries) - [Обработка ошибок](#error-handling) + - [Посредники Guzzle](#guzzle-middleware) - [Параметры Guzzle](#guzzle-options) - [Параллельные запросы](#concurrent-requests) - [Макрокоманды](#macros) @@ -202,7 +203,8 @@ composer require guzzlehttp/guzzle $response = Http::retry(3, 100, throw: false)->post(/* ... */); -> {note} Если все запросы окажутся неуспешными из-за проблемы с соединением, то все равно будет выброшено исключение `Illuminate\Http\Client\ConnectionException`, даже если для аргумента `throw` установлено значение `false`. +> **Предупреждение**\ +> Если все запросы окажутся неуспешными из-за проблемы с соединением, то все равно будет выброшено исключение `Illuminate\Http\Client\ConnectionException`, даже если для аргумента `throw` установлено значение `false`. ### Обработка ошибок @@ -237,6 +239,15 @@ composer require guzzlehttp/guzzle // Выбросить исключение, если произошла ошибка и переданное условие является истинным $response->throwIf($condition); + // Выбросить исключение, если произошла ошибка и переданное замыкание возвращает `true` + $response->throwIf(fn ($response) => true); + + // Выбросить исключение, если произошла ошибка и переданное условие не является истинным + $response->throwUnless($condition); + + // Выбросить исключение, если произошла ошибка и переданное замыкание возвращает `false` + $response->throwUnless(fn ($response) => false); + return $response['user']['id']; Экземпляр `Illuminate\Http\Client\RequestException` имеет свойство `$response`, которое позволит вам проверить возвращенный ответ. @@ -251,6 +262,39 @@ composer require guzzlehttp/guzzle // })->json(); + +### Посредники Guzzle + +Поскольку HTTP-клиент Laravel использует Guzzle, то вы можете воспользоваться [посредниками Guzzle](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html), чтобы манипулировать исходящими запросами или проверять входящие. Чтобы манипулировать исходящим запросом, зарегистрируйте посредника Guzzle с помощью метода `withMiddleware` в сочетании с фабричным методом `mapRequest` посредника Guzzle: + + use GuzzleHttp\Middleware; + use Illuminate\Support\Facades\Http; + use Psr\Http\Message\RequestInterface; + + $response = Http::withMiddleware( + Middleware::mapRequest(function (RequestInterface $request) { + $request = $request->withHeader('X-Example', 'Value'); + + return $request; + }) + )->get('http://example.com'); + +Точно так же вы можете проверить входящий HTTP-ответ, зарегистрировав посредника с помощью метода `withMiddleware` в сочетании с фабричным методом `mapResponse` посредника Guzzle: + + use GuzzleHttp\Middleware; + use Illuminate\Support\Facades\Http; + use Psr\Http\Message\ResponseInterface; + + $response = Http::withMiddleware( + Middleware::mapResponse(function (ResponseInterface $response) { + $header = $response->getHeader('X-Example'); + + // ... + + return $response; + }) + )->get('http://example.com'); + ### Параметры Guzzle @@ -374,7 +418,7 @@ $response = Http::github()->get('/'); ->pushStatus(404), ]); -Когда все ответы в этой последовательности будут использованы, любые дальнейшие запросы приведут к выбросу исключения. Если вы хотите указать ответ по умолчанию, который должен возвращаться, когда последовательность пуста, то используйте метод `whenEmpty`: +Когда все ответы в последовательности будут использованы, тогда любые дальнейшие запросы приведут к выбросу исключения. Если вы хотите указать ответ по умолчанию, который должен возвращаться, когда последовательность пуста, то используйте метод `whenEmpty`: Http::fake([ // Заглушка серии ответов для адресов GitHub ... @@ -474,6 +518,45 @@ $response = Http::github()->get('/'); Http::assertNothingSent(); + +#### Запись запросов/ответов + +Вы можете использовать метод `recorded` для сбора всех запросов и соответствующих им ответов. Метод `recorded` возвращает коллекцию массивов, содержащих экземпляры `Illuminate\Http\Client\Request` и `Illuminate\Http\Client\Response`: + +```php +Http::fake([ + 'https://laravel.com' => Http::response(status: 500), + 'https://nova.laravel.com/' => Http::response(), +]); + +Http::get('https://laravel.com'); +Http::get('https://nova.laravel.com/'); + +$recorded = Http::recorded(); + +[$request, $response] = $recorded[0]; +``` + +Кроме того, метод `recorded` принимает замыкание, которое получит экземпляр `Illuminate\Http\Client\Request` и `Illuminate\Http\Client\Response` и может использоваться для фильтрации пар запрос/ответ на основе ваших ожиданий: + +```php +use Illuminate\Http\Client\Request; +use Illuminate\Http\Client\Response; + +Http::fake([ + 'https://laravel.com' => Http::response(status: 500), + 'https://nova.laravel.com/' => Http::response(), +]); + +Http::get('https://laravel.com'); +Http::get('https://nova.laravel.com/'); + +$recorded = Http::recorded(function (Request $request, Response $response) { + return $request->url() !== 'https://laravel.com' && + $response->successful(); +}); +``` + ## События diff --git a/docs/http-tests.md b/docs/http-tests.md index 7a58fd0..0ee687c 100644 --- a/docs/http-tests.md +++ b/docs/http-tests.md @@ -78,7 +78,8 @@ Laravel предлагает гибкий API в составе вашего п В общем, каждый из ваших тестов должен делать только один запрос к вашему приложению. Выполнение нескольких запросов в одном тестовом методе может спровоцировать непредвиденное поведение. -> {tip} Для удобства посредник CSRF автоматически отключается при запуске тестов. +> **Примечание**\ +> Для удобства посредник CSRF автоматически отключается при запуске тестов. ### Настройка заголовков запросов @@ -151,7 +152,7 @@ Laravel предлагает несколько помощников для вз } } -Сессия Laravel обычно используется для сохранения состояния текущего аутентифицированного пользователя. Вспомогательный метод `actingAs` – это простой способ аутентифицировать конкретного пользователя как текущего. Например, мы можем использовать [фабрику модели](database-testing.md#defining-model-factories) для генерации и аутентификации пользователя: +Сессия Laravel обычно используется для сохранения состояния текущего аутентифицированного пользователя. Вспомогательный метод `actingAs` – это простой способ аутентифицировать конкретного пользователя как текущего. Например, мы можем использовать [фабрику модели](eloquent-factories.md) для генерации и аутентификации пользователя: actingAs($user, 'web') @@ -278,7 +279,8 @@ Laravel также содержит несколько помощников дл $this->assertTrue($response['created']); -> {tip} Метод `assertJson` преобразует ответ в массив и использует `PHPUnit::assertArraySubset` для проверки того, что переданный массив существует в ответе JSON, возвращаемом приложением. Итак, если в ответе JSON есть другие свойства, этот тест все равно будет проходить, пока присутствует переданный фрагмент. +> **Примечание**\ +> Метод `assertJson` преобразует ответ в массив и использует `PHPUnit::assertArraySubset` для проверки того, что переданный массив существует в ответе JSON, возвращаемом приложением. Итак, если в ответе JSON есть другие свойства, этот тест все равно будет проходить, пока присутствует переданный фрагмент. #### Утверждение точных совпадений JSON @@ -362,6 +364,8 @@ Laravel также предлагает прекрасный способ пос ->assertJson(fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) + ->whereNot('status', 'pending') ->missing('password') ->etc() ); @@ -373,6 +377,8 @@ Laravel также предлагает прекрасный способ пос Цель такого поведения – защитить вас от непреднамеренного раскрытия конфиденциальной информации в ваших ответах JSON, заставив вас либо явно сделать утверждение относительно атрибута, либо явно разрешить дополнительные атрибуты с помощью метода `etc`. +Вы должны знать, что отсутствие метода `etc` в цепочке утверждений не гарантирует, что дополнительные атрибуты не будут добавлены во вложенные массивы объекта JSON. Метод `etc` гарантирует отсутствие дополнительных атрибутов только на уровне вложенности, на котором вызывается метод `etc`. + #### Утверждения относительно наличия / отсутствия атрибута JSON @@ -414,6 +420,7 @@ Laravel также предлагает прекрасный способ пос ->first(fn ($json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) ->missing('password') ->etc() ) @@ -440,6 +447,7 @@ Laravel также предлагает прекрасный способ пос ->has('users.0', fn ($json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) ->missing('password') ->etc() ) @@ -453,6 +461,7 @@ Laravel также предлагает прекрасный способ пос ->has('users', 3, fn ($json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->where('email', fn ($email) => str($email)->is('victoria@gmail.com')) ->missing('password') ->etc() ) @@ -586,7 +595,7 @@ Laravel также позволяет отображать шаблоны без $view->assertSee('Taylor'); -Вы можете использовать метод `component` для анализа и отрисовки [компонента Blade](blade.md#components). Как и метод `view`, метод `component` возвращает экземпляр `Illuminate\Testing\TestView`: +Вы можете использовать метод `component` для анализа и отрисовки [компонента Blade](blade.md#components). Метод `component` возвращает экземпляр `Illuminate\Testing\TestComponent`: $view = $this->component(Profile::class, ['name' => 'Taylor']); @@ -639,12 +648,15 @@ Laravel также позволяет отображать шаблоны без - [assertJsonValidationErrors](#assert-json-validation-errors) - [assertJsonValidationErrorFor](#assert-json-validation-error-for) - [assertLocation](#assert-location) +- [assertContent](#assert-content) - [assertNoContent](#assert-no-content) +- [assertStreamedContent](#assert-streamed-content) - [assertNotFound](#assert-not-found) - [assertOk](#assert-ok) - [assertPlainCookie](#assert-plain-cookie) - [assertRedirect](#assert-redirect) - [assertRedirectContains](#assert-redirect-contains) +- [assertRedirectToRoute](#assert-redirect-to-route) - [assertRedirectToSignedRoute](#assert-redirect-to-signed-route) - [assertSee](#assert-see) - [assertSeeInOrder](#assert-see-in-order) @@ -695,7 +707,7 @@ Laravel также позволяет отображать шаблоны без #### assertCookieMissing -Утверждение о том, что ответ не содержит переданный cookie: +Утверждение о том, что ответ не содержит переданный файл cookie: $response->assertCookieMissing($cookieName); @@ -813,7 +825,8 @@ Laravel также позволяет отображать шаблоны без $response->assertJsonMissingValidationErrors($keys); -> {tip} Более общий метод [assertValid](#assert-valid) может использоваться для утверждения о том, что ответ не содержит ошибок валидации, которые были возвращены как JSON **и**, что ошибки не были записаны в сессию. +> **Примечание**\ +> Более общий метод [`assertValid`](#assert-valid) может использоваться для утверждения о том, что в ответе нет ошибок валидации, которые были возвращены в виде структуры JSON **и**, что ошибки не были записаны в сессию. #### assertJsonPath @@ -824,7 +837,7 @@ Laravel также позволяет отображать шаблоны без Например, если ваше приложение возвращает следующий ответ JSON: -```js +```json { "user": { "name": "Steve Schoger" @@ -845,7 +858,7 @@ Laravel также позволяет отображать шаблоны без Например, если ваше приложение возвращает следующий ответ JSON: -```js +```json { "user": { "name": "Steve Schoger" @@ -866,7 +879,7 @@ Laravel также позволяет отображать шаблоны без Например, если ответ JSON, возвращаемый вашим приложением, содержит следующие данные: -```js +```json { "user": { "name": "Steve Schoger" @@ -884,7 +897,7 @@ Laravel также позволяет отображать шаблоны без Иногда ответы JSON, возвращаемые вашим приложением, могут содержать массивы объектов: -```js +```json { "user": [ { @@ -920,7 +933,8 @@ Laravel также позволяет отображать шаблоны без $response->assertJsonValidationErrors(array $data, $responseKey = 'errors'); -> {tip} Более общий метод [assertInvalid](#assert-invalid) может использоваться для утверждения о том, что ответ содержит ошибки валидации, которые были возвращены как JSON **или**, что ошибки были записаны в сессию. +> **Примечание**\ +> Более общий метод [`assertInvalid`](#assert-invalid) может использоваться для утверждения о том, что ответ содержит ошибки валидации, которые были возвращены в виде структуры JSON **или**, что ошибки были записаны в сессию. #### assertJsonValidationErrorFor @@ -936,6 +950,13 @@ Laravel также позволяет отображать шаблоны без $response->assertLocation($uri); + +#### assertContent + +Утверждение о том, что переданная строка соответствует содержимому ответа: + + $response->assertContent($value); + #### assertNoContent @@ -943,6 +964,13 @@ Laravel также позволяет отображать шаблоны без $response->assertNoContent($status = 204); + +#### assertStreamedContent + +Утверждение о том, что переданная строка соответствует содержимому потока в ответе: + + $response->assertStreamedContent($val + #### assertNotFound @@ -978,6 +1006,13 @@ Laravel также позволяет отображать шаблоны без $response->assertRedirectContains($string); + +#### assertRedirectToRoute + +Утверждение о том, что ответ является перенаправлением на указанный [именованный маршрут](routing.md#named-routes): + + $response->assertRedirectToRoute($name = null, $parameters = []); + #### assertRedirectToSignedRoute @@ -1072,7 +1107,8 @@ Laravel также позволяет отображать шаблоны без 'name' => 'The given name was invalid.' ]); -> {tip} Более общий метод [`assertInvalid`](#assert-invalid) может использоваться для утверждения о том, что в ответе есть ошибки валидации, которые были возвращены в виде структуры JSON **или**, что ошибки были переданы в хранилище сессии. +> **Примечание**\ +> Более общий метод [`assertInvalid`](#assert-invalid) может использоваться для утверждения о том, что ответ содержит ошибки валидации, которые были возвращены в виде структуры JSON **или**, что ошибки были записаны в сессию. #### assertSessionHasErrorsIn @@ -1095,7 +1131,8 @@ Laravel также позволяет отображать шаблоны без $response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default'); -> {tip} Более общий метод [`assertValid`](#assert-valid) может использоваться для утверждения о том, что в ответе нет ошибок валидации, которые были возвращены в виде структуры JSON **и**, что ошибки не были переданы в хранилище сессии. +> **Примечание**\ +> Более общий метод [`assertValid`](#assert-valid) может использоваться для утверждения о том, что в ответе нет ошибок валидации, которые были возвращены в виде структуры JSON **и**, что ошибки не были записаны в сессию. #### assertSessionMissing diff --git a/docs/installation.md b/docs/installation.md index 353c7d6..06ff36f 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -3,14 +3,14 @@ - [Встречайте Laravel](#meet-laravel) - [Почему именно Laravel?](#why-laravel) - [Ваш первый проект на Laravel](#your-first-laravel-project) +- [Laravel и Docker](#laravel-and-docker) - [Начало работы в macOS](#getting-started-on-macos) - [Начало работы в Windows](#getting-started-on-windows) - [Начало работы в Linux](#getting-started-on-linux) - [Выбор служб Sail](#choosing-your-sail-services) - - [Установка через Composer](#installation-via-composer) - [Начальная конфигурация](#initial-configuration) - [Конфигурация на основе окружения](#environment-based-configuration) - - [Конфигурация каталога](#directory-configuration) + - [Базы данных и миграции](#databases-and-migrations) - [Следующие шаги](#next-steps) - [Laravel как клиент-серверный фреймворк](#laravel-the-fullstack-framework) - [Laravel в качестве сервера API](#laravel-the-api-backend) @@ -24,6 +24,9 @@ Laravel стремится обеспечить потрясающий опыт Независимо от того, новичок ли вы в веб-фреймворках PHP или имеете многолетний опыт, Laravel – это фреймворк, который может расти вместе с вами. Мы поможем вам сделать первые шаги в качестве веб-разработчика или подскажем, как вы поднимите свой опыт на новый уровень. Нам не терпится увидеть, что вы построите. +> **Примечание**\ +> Новичок в Laravel? Посетите [Laravel Bootcamp](https://bootcamp.laravel.com) для практического ознакомления с фреймворком, пока мы познакомим вас с созданием вашего первого приложения Laravel. + ### Почему именно Laravel? @@ -48,13 +51,46 @@ Laravel объединяет лучшие пакеты в экосистеме P ## Ваш первый проект на Laravel -Мы хотим, чтобы начать работу с Laravel было как можно проще. Существует множество вариантов разработки и запуска проекта Laravel на вашей локальной машине. Хотя вы, возможно, захотите изучить эти варианты позже, но Laravel предлагает [Sail](sail.md) – встроенное решение для запуска вашего проекта Laravel с помощью [Docker](https://www.docker.com). +Перед созданием вашего первого проекта Laravel вы должны убедиться, что на вашем локальном компьютере установлены PHP и [Composer](https://getcomposer.org). Если вы разрабатываете на macOS, PHP и Composer можно установить через [Homebrew](https://brew.sh/). Кроме того, мы рекомендуем [установить Node и NPM](https://nodejs.org). + +После того, как вы установили PHP и Composer, вы можете создать новый проект Laravel с помощью команды `create-project` Composer: + +```shell +composer create-project laravel/laravel example-app +``` + +Или вы можете создавать новые проекты Laravel, установив глобально установщик Laravel через Composer: + +```shell +composer global require laravel/installer + +laravel new example-app +``` + +После создания проекта запустите локальный сервер разработки Laravel с помощью команды `serve` интерфейса командной строки Laravel Artisan: + +```shell +cd example-app + +php artisan serve +``` + +Как только вы запустите сервер разработки Artisan, ваше приложение будет доступно в вашем веб-браузере по адресу `http://localhost:8000`. Теперь вы готовы [сделать следующие шаги в экосистеме Laravel](#next-steps). Конечно, вы также можете [настроить базу данных](#databases-and-migrations). + +> **Примечание**\ +> Если вы хотите получить преимущество перед созданием своего приложения, ознакомьтесь с одним из наших официальных [стартовых комплектов приложений](starter-kits.md). Стартовые комплекты Laravel обеспечивают каркас аутентификации как серверной части, так и внешнего интерфейса для вашего нового приложения Laravel. + + +## Laravel и Docker + +Мы хотим, чтобы начать работу с Laravel было как можно проще, независимо от предпочитаемой вами операционной системы. Таким образом, существует множество вариантов разработки и запуска проекта Laravel на вашем локальном компьютере. Хотя вы, возможно, захотите изучить эти варианты позже, но Laravel предлагает [Sail](sail.md) – встроенное решение для запуска вашего проекта Laravel с помощью [Docker](https://www.docker.com). Docker – это инструмент для запуска приложений и служб в небольших, легких «контейнерах», которые не мешают установленному на вашей локальной машине программному обеспечению или его конфигурации. Это означает, что вам не нужно беспокоиться о конфигурировании или настройке сложных инструментов разработки, таких как веб-серверы и базы данных на вашей локальной машине. Для начала вам нужно всего лишь установить [Docker Desktop](https://www.docker.com/products/docker-desktop). Laravel Sail – это легкий интерфейс командной строки для взаимодействия с конфигурацией Docker по умолчанию в Laravel. Sail обеспечивает отличную отправную точку для создания приложения Laravel с использованием PHP, MySQL и Redis без предварительного опыта работы с Docker. -> {tip} Уже знакомы с Docker? Не волнуйтесь! Все в Sail можно перенастроить с помощью файла `docker-compose.yml`, входящего в Laravel. +> **Примечание**\ +> Уже знакомы с Docker? Не волнуйтесь! Все в Sail можно перенастроить с помощью файла `docker-compose.yml`, входящего в Laravel. ### Начало работы в macOS @@ -67,6 +103,8 @@ curl -s https://laravel.build/example-app | bash Конечно, вы можете изменить `example-app` в этом URL на что угодно – просто убедитесь, что имя приложения содержит только буквенно-цифровые символы, дефисы и символы подчеркивания. Каталог приложения Laravel будет создан в каталоге, из которого вы выполняете команду. +Установка Sail может занять несколько минут, пока контейнеры приложений Sail собираются на вашем локальном компьютере. + После создания проекта вы можете перейти в каталог приложения и запустить Laravel Sail. Laravel Sail предлагает простой интерфейс командной строки для взаимодействия с конфигурацией Docker по умолчанию в Laravel: ```shell @@ -75,18 +113,18 @@ cd example-app ./vendor/bin/sail up ``` -При первом запуске команды `up` Sail на вашей локальной машине будут созданы контейнеры приложений Sail. Это может занять несколько минут. **Не волнуйтесь, последующие попытки запустить Sail будут намного быстрее**. - После запуска контейнеров приложения Docker, вы можете получить доступ к приложению в своем веб-браузере по адресу: http://localhost. -> {tip} Чтобы продолжить изучение Laravel Sail, просмотрите его [полную документацию](sail.md). +> **Примечание**\ +> Чтобы продолжить изучение Laravel Sail, просмотрите его [полную документацию](sail.md). ### Начало работы в Windows Прежде чем мы создадим новое приложение Laravel на вашей локальной машине с Windows, обязательно установите [Docker Desktop](https://www.docker.com/products/docker-desktop). Затем вы должны убедиться, что подсистема Windows для Linux 2 (WSL2) установлена и включена. WSL позволяет запускать двоичные исполняемые файлы Linux прямо в Windows 10. Информацию о том, как установить и включить WSL2, можно найти в [документации Среда разработки](https://docs.microsoft.com/ru-ru/windows/wsl/install-win10). -> {tip} После установки и включения WSL2 вы должны убедиться, что Docker Desktop [настроен на использование серверной части WSL2](https://docs.docker.com/docker-for-windows/wsl/). +> **Примечание**\ +> После установки и включения WSL2 вы должны убедиться, что Docker Desktop [настроен на использование серверной части WSL2](https://docs.docker.com/docker-for-windows/wsl/). Теперь вы готовы создать свой первый проект Laravel. Запустите [Терминал Windows](https://www.microsoft.com/ru-ru/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) и начните новый сеанс терминала для вашей операционной системы WSL2 Linux. Затем вы можете использовать простую команду терминала для создания нового проекта Laravel. Например, чтобы создать новое приложение Laravel в каталоге с именем `example-app`, вы можете запустить следующую команду в своем терминале: @@ -96,6 +134,8 @@ curl -s https://laravel.build/example-app | bash Конечно, вы можете изменить `example-app` в этом URL на что угодно – просто убедитесь, что имя приложения содержит только буквенно-цифровые символы, дефисы и символы подчеркивания. Каталог приложения Laravel будет создан в каталоге, из которого вы выполняете команду. +Установка Sail может занять несколько минут, пока контейнеры приложений Sail собираются на вашем локальном компьютере. + После создания проекта вы можете перейти в каталог приложения и запустить Laravel Sail. Laravel Sail предлагает простой интерфейс командной строки для взаимодействия с конфигурацией Docker по умолчанию в Laravel: ```shell @@ -104,11 +144,10 @@ cd example-app ./vendor/bin/sail up ``` -При первом запуске команды `up` Sail на вашей локальной машине будут созданы контейнеры приложений Sail. Это может занять несколько минут. **Не волнуйтесь, последующие попытки запустить Sail будут намного быстрее**. - После запуска контейнеров приложения Docker, вы можете получить доступ к приложению в своем веб-браузере по адресу: http://localhost. -> {tip} Чтобы продолжить изучение Laravel Sail, просмотрите его [полную документацию](sail.md). +> **Примечание**\ +> Чтобы продолжить изучение Laravel Sail, просмотрите его [полную документацию](sail.md). #### Разработка в подсистеме WSL2 @@ -127,6 +166,8 @@ curl -s https://laravel.build/example-app | bash Конечно, вы можете изменить `example-app` в этом URL на что угодно – просто убедитесь, что имя приложения содержит только буквенно-цифровые символы, дефисы и символы подчеркивания. Каталог приложения Laravel будет создан в каталоге, из которого вы выполняете команду. +Установка Sail может занять несколько минут, пока контейнеры приложений Sail собираются на вашем локальном компьютере. + После создания проекта вы можете перейти в каталог приложения и запустить Laravel Sail. Laravel Sail предлагает простой интерфейс командной строки для взаимодействия с конфигурацией Docker по умолчанию в Laravel: ```shell @@ -135,11 +176,10 @@ cd example-app ./vendor/bin/sail up ``` -При первом запуске команды `up` Sail на вашей локальной машине будут созданы контейнеры приложений Sail. Это может занять несколько минут. **Не волнуйтесь, последующие попытки запустить Sail будут намного быстрее**. - После запуска контейнеров приложения Docker, вы можете получить доступ к приложению в своем веб-браузере по адресу: http://localhost. -> {tip} Чтобы продолжить изучение Laravel Sail, просмотрите его [полную документацию](sail.md). +> **Примечание**\ +> Чтобы продолжить изучение Laravel Sail, просмотрите его [полную документацию](sail.md). ### Выбор служб Sail @@ -158,97 +198,52 @@ curl -s "https://laravel.build/example-app?with=mysql,redis" | bash curl -s "https://laravel.build/example-app?with=mysql,redis&devcontainer" | bash ``` - -### Установка через Composer - -Если на вашей локальной машине уже установлены PHP и Composer, то вы можете создать новый проект Laravel напрямую с помощью Composer. После того, как приложение было создано, вы можете запустить локальный сервер разработки Laravel с помощью команды `serve` Artisan CLI: - -```shell -composer create-project laravel/laravel example-app - -cd example-app - -php artisan serve -``` - -После того, как вы запустили сервер разработки Artisan, вы можете получить доступ к своему приложению по адресу `http://localhost:8000`. - - -#### Установщик Laravel - -В качестве альтернативы, вы можете использовать установщик Laravel, включив его в глобальную зависимость Composer: - -```shell -composer global require laravel/installer - -laravel new example-app + +## Начальная конфигурация -cd example-app +Все файлы конфигурации для фреймворка Laravel хранятся в каталоге `config`. Каждый параметр имеет комментарии, поэтому не стесняйтесь просматривать файлы и знакомиться с доступными вам вариантами. -php artisan serve -``` +Laravel практически не требует дополнительной настройки из коробки. Вы можете начать разработку! Однако вы можете просмотреть файл `config/app.php` и его комментарии. Он содержит несколько параметров, таких как часовой пояс (`timezone`) и языковой стандарт (`locale`), которые вы можете изменить в соответствии с вашим приложением. -Чтобы исполняемый файл `laravel` мог быть обнаружен вашей системой, удостоверьтесь в правильном расположении каталога `bin` менеджера пакетов Composer, задаваемый системной переменной `$PATH`. Расположение каталога зависит от вашей операционной системы, но типичными могут быть: + +### Конфигурация на основе окружения - +Поскольку многие значения параметров конфигурации Laravel могут различаться в зависимости от того, работает ли ваше приложение на локальной машине или на эксплуатационном веб-сервере, многие важные значения конфигурации определяются с помощью файла `.env`, существующий в корне вашего приложения. -- macOS: `$HOME/.composer/vendor/bin` -- Windows: `%USERPROFILE%\AppData\Roaming\Composer\vendor\bin` -- GNU / Linux Distributions: `$HOME/.config/composer/vendor/bin` или `$HOME/.composer/vendor/bin` +Ваш файл `.env` не должен быть привязан к системе контроля версий вашего приложения, поскольку каждому разработчику / серверу, использующему ваше приложение, может потребоваться другая конфигурация окружения. Более того, это будет угрозой безопасности в случае, если злоумышленник получит доступ к вашему репозиторию системы управления версиями, поскольку любые конфиденциальные учетные данные будут раскрыты. - +> **Примечание**\ +> Для получения дополнительной информации о конфигурации на основе файла `.env` и окружения ознакомьтесь с полной [документацией по конфигурации](configuration.md#environment-configuration). -Для удобства установщик Laravel также может создать репозиторий Git для вашего нового проекта. Чтобы указать, что вы хотите создать репозиторий Git, передайте флаг `--git` при создании нового проекта: + +### Базы данных и миграции -```shell -laravel new example-app --git -``` +Теперь, когда вы создали свое приложение Laravel, вы, вероятно, захотите сохранить некоторые данные в базе данных. По умолчанию файл конфигурации `.env` вашего приложения указывает, что Laravel будет взаимодействовать с базой данных MySQL и будет обращаться к базе данных по адресу `127.0.0.1`. Если вы разрабатываете на macOS и вам необходимо установить MySQL, Postgres или Redis локально, то вам может быть удобно использовать [DBngin](https://dbngin.com/). -Эта команда инициализирует новый репозиторий Git для вашего проекта и автоматически зафиксирует базовый каркас Laravel. Флаг `--git` предполагает, что вы правильно установили и настроили Git. Можно также использовать параметр `--branch`, чтобы задать имя ответвления: +Если вы не хотите устанавливать MySQL или Postgres на свой локальный компьютер, то вы всегда можете использовать базу данных [SQLite](https://www.sqlite.org/index.html). SQLite — это небольшой, быстрый, автономный движок базы данных. Для начала создайте базу данных SQLite, создав пустой файл SQLite. Как правило, этот файл будет находиться в каталоге `database` вашего приложения Laravel: ```shell -laravel new example-app --git --branch="main" +touch database/database.sqlite ``` -Вместо использования флага `--git` вы можете использовать параметр `--github`, чтобы создать репозиторий Git и, соответствующий ему, частный репозиторий на GitHub: +Затем обновите файл конфигурации `.env`, чтобы использовать драйвер базы данных `sqlite`. Вы можете удалить другие параметры конфигурации базы данных: -```shell -laravel new example-app --github +```ini +DB_CONNECTION=sqlite # [tl! add] +DB_CONNECTION=mysql # [tl! remove] +DB_HOST=127.0.0.1 # [tl! remove] +DB_PORT=3306 # [tl! remove] +DB_DATABASE=laravel # [tl! remove] +DB_USERNAME=root # [tl! remove] +DB_PASSWORD= # [tl! remove] ``` -Созданный репозиторий будет доступен по адресу `https://github.com//example-app`. Параметр `--github` предполагает, что вы правильно установили [GitHub CLI](https://cli.github.com) и прошли аутентификацию с помощью интерфейса командной строки. Кроме того, у вас должен быть установлен и правильно настроен [git](https://git-scm.com/). При необходимости вы можете передать [дополнительные параметры и флаги](https://cli.github.com/manual/gh_repo_create), поддерживаемые GitHub CLI: +После того, как вы настроили свою базу данных SQLite, вы можете запустить [миграции базы данных](migrations.md) вашего приложения, которые создадут таблицы базы данных вашего приложения: ```shell -laravel new example-app --github="--public" +php artisan migrate ``` -Можно использовать параметр `--organization` для создания репозитория под определенной организацией GitHub: - -```shell -laravel new example-app --github="--public" --organization="laravel" -``` - - -## Начальная конфигурация - -Все файлы конфигурации для фреймворка Laravel хранятся в каталоге `config`. Каждый параметр имеет комментарии, поэтому не стесняйтесь просматривать файлы и знакомиться с доступными вам вариантами. - -Laravel практически не требует дополнительной настройки из коробки. Вы можете начать разработку! Однако вы можете просмотреть файл `config/app.php` и его комментарии. Он содержит несколько параметров, таких как часовой пояс и локаль, которые вы можете изменить в соответствии с вашим приложением. - - -### Конфигурация на основе окружения - -Поскольку многие значения параметров конфигурации Laravel могут различаться в зависимости от того, работает ли ваше приложение на локальной машине или на эксплуатационном веб-сервере, многие важные значения конфигурации определяются с помощью файла `.env`, существующий в корне вашего приложения. - -Ваш файл `.env` не должен быть привязан к системе контроля версий вашего приложения, поскольку каждому разработчику / серверу, использующему ваше приложение, может потребоваться другая конфигурация окружения. Более того, это будет угрозой безопасности в случае, если злоумышленник получит доступ к вашему репозиторию системы управления версиями, поскольку любые конфиденциальные учетные данные будут раскрыты. - -> {tip} Для получения дополнительной информации о конфигурации на основе файла `.env` и окружения ознакомьтесь с полной [документацией по конфигурации](configuration.md#environment-configuration). - - -### Конфигурация каталога - -Laravel всегда должен обслуживаться из корня «веб-каталога», настроенного для вашего веб-сервера. Вы не должны пытаться обслуживать приложение Laravel из поддиректории относительно «веб-каталога». Такая попытка может открыть доступ к конфиденциальным файлам, существующим в вашем приложении. - ## Следующие шаги @@ -259,6 +254,7 @@ Laravel всегда должен обслуживаться из корня « - [Жизненный цикл запроса](lifecycle.md) - [Конфигурирование](configuration.md) - [Структура каталогов](structure.md) +- [Внешний интерфейс приложения](frontend.md) - [Контейнер служб](container.md) - [Фасады](facades.md) @@ -266,16 +262,20 @@ Laravel всегда должен обслуживаться из корня « То, как вы хотите использовать Laravel, также будет определять следующие шаги на вашем пути. Существует множество способов использования Laravel, и мы рассмотрим два основных варианта использования фреймворка ниже. +> **Примечание**\ +> Новичок в Laravel? Посетите [Laravel Bootcamp](https://bootcamp.laravel.com) для практического ознакомления с фреймворком, пока мы познакомим вас с созданием вашего первого приложения Laravel. + ### Laravel как клиент-серверный фреймворк -Laravel может служить клиент-серверным фреймворком. Под «клиент-серверным фреймворком» мы подразумеваем, что вы собираетесь использовать Laravel для маршрутизации запросов к вашему приложению и отрисовки интерфейса через [шаблоны Blade](blade.md) или гибридную технологию одностраничного приложения, такой как [Inertia.js](https://inertiajs.com). Это наиболее распространенный способ использования фреймворка Laravel и, на наш взгляд, самый продуктивный способ использования Laravel. +Laravel может служить клиент-серверным фреймворком. Под «клиент-серверным фреймворком» мы подразумеваем, что вы собираетесь использовать Laravel для маршрутизации запросов к вашему приложению и отрисовки интерфейса через [шаблоны Blade](blade.md) или гибридную технологию одностраничного приложения, такой как [Inertia](https://inertiajs.com). Это наиболее распространенный способ использования фреймворка Laravel и, на наш взгляд, самый продуктивный способ использования Laravel. -Если вы планируете использовать Laravel именно таким образом, вы можете ознакомиться с нашей документацией по [маршрутизации](routing.md), [представлениям](views.md) или [Eloquent ORM](eloquent.md). Кроме того, вам может быть интересно узнать о таких пакетах сообщества, как [Livewire](https://laravel-livewire.com) и [Inertia.js](https://inertiajs.com). Эти пакеты позволяют использовать Laravel в качестве фреймворка полного стека, при этом пользуясь многими преимуществами UI, предоставляемыми одностраничными JavaScript-приложениями. +Если вы планируете использовать Laravel именно таким образом, то вы можете ознакомиться с нашей документацией по [разработке внешнего интерфейса](frontend.md), [маршрутизации](routing.md), [представлениям](views.md) или [Eloquent ORM](eloquent.md). Кроме того, вам может быть интересно узнать о таких пакетах сообщества, как [Livewire](https://laravel-livewire.com) и [Inertia](https://inertiajs.com). Эти пакеты позволяют использовать Laravel в качестве фреймворка полного стека, при этом пользуясь многими преимуществами UI, предоставляемыми одностраничными JavaScript-приложениями. -Если вы используете Laravel в качестве фреймворка полного стека, мы также настоятельно рекомендуем вам научиться компилировать CSS и JavaScript вашего приложения с помощью [Laravel Mix](mix.md). +Если вы используете Laravel в качестве фреймворка полного стека, мы также настоятельно рекомендуем вам научиться компилировать CSS и JavaScript вашего приложения с помощью [Vite](vite.md). -> {tip} Если вы хотите получить преимущество перед созданием своего приложения, ознакомьтесь с одним из наших официальных [стартовых комплектов приложений](starter-kits.md). +> **Примечание**\ +> Если вы хотите получить преимущество перед созданием своего приложения, ознакомьтесь с одним из наших официальных [стартовых комплектов приложений](starter-kits.md). ### Laravel в качестве сервера API @@ -284,4 +284,5 @@ Laravel также может служить серверной частью API Если вы планируете использовать Laravel именно так, то вы можете ознакомиться с нашей документацией по [маршрутизации](routing.md), пакету [Laravel Sanctum](sanctum.md) и [Eloquent ORM](eloquent.md). -> {tip} Вы хотите создать бэкэнд на Laravel и интерфейс на Next.js? Laravel Breeze предлагает [стек API](starter-kits.md#breeze-and-next), а также [реализацию внешнего интерфейса Next.js](https://github.com/laravel/breeze-next ), чтобы вы могли начать работу за считанные минуты. +> **Примечание**\ +> Вы хотите создать серверную часть на Laravel, а интерфейс на Next.js? Laravel Breeze предлагает [стек API](starter-kits.md#breeze-and-next), а также [реализацию внешнего интерфейса Next.js](https://github.com/laravel/breeze-next ), чтобы вы могли начать работу за считанные минуты. diff --git a/docs/localization.md b/docs/localization.md index 5783e2a..f237bc3 100644 --- a/docs/localization.md +++ b/docs/localization.md @@ -87,7 +87,8 @@ Laravel предлагает два способа управления стро // ... } -> {note} Если вы изменяете язык построителя слов во множественном числе, то вы должны явно определять [имена таблиц](eloquent.md#table-names) моделей Eloquent. +> **Предупреждение**\ +> Если вы изменяете язык построителя слов во множественном числе, то вы должны явно определять [имена таблиц](eloquent.md#table-names) моделей Eloquent. ## Определение строк перевода @@ -113,7 +114,8 @@ Laravel предлагает два способа управления стро 'welcome' => 'Welcome to our application!', ]; -> {note} Для языков, отличающихся территориально, вы должны назвать языковые каталоги в соответствии со стандартом ISO 15897. Например, для британского английского следует использовать `en_GB`, а не `en-gb`. +> **Предупреждение**\ +> Для языков, отличающихся территориально, вы должны назвать языковые каталоги в соответствии со стандартом ISO 15897. Например, для британского английского следует использовать `en_GB`, а не `en-gb`. ### Использование строк перевода в качестве ключей diff --git a/docs/logging.md b/docs/logging.md index 6bc3fd6..58356cf 100644 --- a/docs/logging.md +++ b/docs/logging.md @@ -59,7 +59,8 @@ `stack` | Обертка для облегчения создания «многоканальных» каналов. `syslog` | Драйвер Monolog на основе `SyslogHandler`. -> {tip} Изучите документацию по [расширенной настройке канала](#monolog-channel-customization), чтобы узнать больше о драйверах `monolog` и `custom`. +> **Примечание**\ +> Изучите документацию по [расширенной настройке канала](#monolog-channel-customization), чтобы узнать больше о драйверах `monolog` и `custom`. ### Предварительная подготовка канала @@ -143,7 +144,7 @@ PHP, Laravel и другие библиотеки часто уведомляю #### Уровни регистрации сообщений -Обратите внимание на параметр конфигурации `level`, присутствующий в конфигурациях каналов `syslog` и `slack` в приведенном выше примере. Эта опция определяет минимальный «уровень» сообщения, которое должно быть зарегистрировано каналом. Monolog, на котором работают службы ведения журналов Laravel, предлагает все уровни регистрации сообщений, определенные в спецификации [RFC 5424 specification](https://tools.ietf.org/html/rfc5424): **emergency**, **alert**, **critical**, **error**, **warning**, **notice**, **info**, и **debug**. +Обратите внимание на параметр конфигурации `level`, присутствующий в конфигурациях каналов `syslog` и `slack` в приведенном выше примере. Эта опция определяет минимальный «уровень» сообщения, которое должно быть зарегистрировано каналом. Monolog, на котором работают службы ведения журналов Laravel, предлагает все уровни регистрации сообщений, определенные в спецификации [RFC 5424 specification](https://tools.ietf.org/html/rfc5424). В порядке убывания серьезности уровни регистрации: **emergency**, **alert**, **critical**, **error**, **warning**, **notice**, **info** и **debug**. Итак, представьте, что мы регистрируем сообщение, используя метод `debug`: @@ -336,7 +337,8 @@ PHP, Laravel и другие библиотеки часто уведомляю } } -> {tip} Все ваши классы, определенные в опции `tap`, извлекаются через [контейнер служб](container.md) Laravel, поэтому будут автоматически внедрены любые объявленные зависимости конструктора. +> **Примечание**\ +> Все ваши классы, определенные в опции `tap`, извлекаются через [контейнер служб](container.md) Laravel, поэтому будут автоматически внедрены любые объявленные зависимости конструктора. ### Создание обработчика каналов Monolog diff --git a/docs/mail.md b/docs/mail.md index 34aa4b4..6c9a265 100644 --- a/docs/mail.md +++ b/docs/mail.md @@ -11,6 +11,8 @@ - [Данные шаблона](#view-data) - [Вложения](#attachments) - [Встраиваемые вложения](#inline-attachments) + - [Прикрепляемые объекты](#attachable-objects) + - [Заголовки](#headers) - [Теги и метаданные](#tags-and-metadata) - [Настройка сообщения Symfony](#customizing-the-symfony-message) - [Отправления с разметкой Markdown](#markdown-mailables) @@ -169,29 +171,44 @@ php artisan make:mail OrderShipped ## Написание отправлений -После того, как вы создали почтовый класс, откройте его, чтобы мы могли изучить его содержимое. Во-первых, обратите внимание, что вся конфигурация почтового класса выполняется в методе `build`. В этом методе вы можете вызывать различные методы, такие как `from`, `subject`, `view` и `attach`, для настройки представления и доставки электронного письма. +После того, как вы создали почтовый класс, откройте его, чтобы мы могли изучить его содержимое. Конфигурация почтового класса `Mailable` выполняется несколькими методами: `envelope`, `content` и `attachments`. -> {tip} Вы можете указывать тип зависимостей в методе `build` почтового отправления. [Контейнер служб](container.md) автоматически внедрит любые необходимые зависимости. +Метод `envelope` возвращает объект `Illuminate\Mail\Mailables\Envelope`, который определяет тему и, иногда, получателей сообщения. Метод `content` возвращает объект `Illuminate\Mail\Mailables\Content`, который определяет [шаблон Blade](blade.md), используемый для создания содержимого сообщения. ### Конфигурирование отправителя - -#### Использование метода `from` + +#### Использование конверта -Во-первых, давайте рассмотрим настройку отправителя электронного письма. Или, другими словами, от кого будет электронное письмо. Настроить отправителя можно двумя способами. Во-первых, вы можете использовать метод `from` в методе `build` вашего почтового класса: +Во-первых, давайте рассмотрим настройку отправителя электронного письма. Или, другими словами, от кого будет электронное письмо. Настроить отправителя можно двумя способами. Во-первых, вы можете указать адрес `from` на конверте вашего сообщения: + + use Illuminate\Mail\Mailables\Address; + use Illuminate\Mail\Mailables\Envelope; /** - * Создать сообщение. + * Получить конверт сообщения. * - * @return $this + * @return \Illuminate\Mail\Mailables\Envelope */ - public function build() + public function envelope() { - return $this->from('example@example.com', 'Example') - ->view('emails.orders.shipped'); + return new Envelope( + from: new Address('jeffrey@example.com', 'Jeffrey Way'), + subject: 'Order Shipped', + ); } +Если хотите, вы также можете указать адрес `replyTo`: + + return new Envelope( + from: new Address('jeffrey@example.com', 'Jeffrey Way'), + replyTo: [ + new Address('taylor@example.com', 'Taylor Otwell'), + ], + subject: 'Order Shipped', + ); + #### Использование глобального адреса `from` @@ -206,36 +223,48 @@ php artisan make:mail OrderShipped ### Конфигурирование шаблона -Внутри метода `build` почтового класса вы можете использовать метод `view`, чтобы указать, какой шаблон следует использовать при отрисовки содержимого электронного письма. Поскольку каждое электронное письмо обычно использует [шаблон Blade](blade.md) для визуализации своего содержимого, вы получаете всю мощь и удобство механизма шаблонов Blade при создании HTML-кода электронной почты: +В методе `content` почтового класса вы можете определить `view` или какой шаблон следует использовать при отображении содержимого электронного письма. Поскольку каждое электронное письмо обычно использует [шаблон Blade](blade.md) для визуализации своего содержимого, вы получаете всю мощь и удобство механизма шаблонов Blade при создании HTML-кода электронной письма: /** - * Создать сообщение. + * Получить определение содержимого сообщения. * - * @return $this + * @return \Illuminate\Mail\Mailables\Content */ - public function build() + public function content() { - return $this->view('emails.orders.shipped'); + return new Content( + view: 'emails.orders.shipped', + ); } -> {tip} Вы можете создать каталог `resources/views/emails` для размещения всех ваших шаблонов электронной почты; однако, вы можете размещать их где угодно в каталоге `resources/views`. +> **Примечание**\ +> Вы можете создать каталог `resources/views/emails` для размещения всех ваших шаблонов электронной почты; но вы можете размещать их где угодно в каталоге `resources/views`. #### Письма с обычным текстом -Если вы хотите определить текстовую версию вашего электронного письма, то вы можете использовать метод `text`. Как и метод `view`, метод `text` принимает имя шаблона, которое будет использоваться для отображения содержимого электронного письма. Вы можете определить как HTML, так и текстовую версию вашего сообщения: +Если вы хотите определить текстовую версию вашего электронного письма, вы можете указать шаблон простого текста при создании определения содержания сообщения `Content`. Как и параметр `view`, параметр `text` должен быть именем шаблона, который будет использоваться для отображения содержимого электронного письма. Вы можете определить как HTML, так и текстовую версию вашего сообщения: /** - * Создать сообщение. + * Получить определение содержимого сообщения. * - * @return $this + * @return \Illuminate\Mail\Mailables\Content */ - public function build() + public function content() { - return $this->view('emails.orders.shipped') - ->text('emails.orders.shipped_plain'); + return new Content( + view: 'emails.orders.shipped', + text: 'emails.orders.shipped-text' + ); } +Для ясности параметр `html` можно использовать как псевдоним параметра `view`: + + return new Content( + html: 'emails.orders.shipped', + text: 'emails.orders.shipped-text' + ); + ### Данные шаблона @@ -251,6 +280,7 @@ php artisan make:mail OrderShipped use App\Models\Order; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; + use Illuminate\Mail\Mailables\Content; use Illuminate\Queue\SerializesModels; class OrderShipped extends Mailable @@ -276,13 +306,15 @@ php artisan make:mail OrderShipped } /** - * Создать сообщение. + * Получить определение содержимого сообщения. * - * @return $this + * @return \Illuminate\Mail\Mailables\Content */ - public function build() + public function content() { - return $this->view('emails.orders.shipped'); + return new Content( + view: 'emails.orders.shipped', + ); } } @@ -292,10 +324,10 @@ php artisan make:mail OrderShipped Price: {{ $order->price }} - -#### Передача данных шаблону через метод `with` + +#### Передача данных шаблону через параметр `with` -Если вы хотите настроить формат данных вашего электронного письма перед их отправкой в шаблон, то вы можете вручную передать свои данные в шаблон с помощью метода `with`. Как правило, вы по-прежнему будете передавать данные через конструктор почтового класса; однако, вы должны установить для этих данных свойства `protected` или `private`, чтобы данные не были автоматически доступны для шаблона. Затем при вызове метода `with` передайте массив, доступных для шаблона данных: +Если вы хотите настроить формат данных вашего электронного письма перед их отправкой в шаблон, то вы можете вручную передать свои данные в шаблон с помощью параметра `with` определения `Content`. Как правило, вы по-прежнему будете передавать данные через конструктор почтового класса; однако вы должны установить для этих данных свойства `protected` или `private`, чтобы данные не стали автоматически доступными для шаблона: view('emails.orders.shipped') - ->with([ - 'orderName' => $this->order->name, - 'orderPrice' => $this->order->price, - ]); + return new Content( + view: 'emails.orders.shipped', + with: [ + 'orderName' => $this->order->name, + 'orderPrice' => $this->order->price, + ], + ); } } @@ -352,95 +387,103 @@ php artisan make:mail OrderShipped ### Вложения -Чтобы добавить вложения к электронному письму, используйте метод `attach` в методе `build` почтового класса. Метод `attach` принимает полный путь к файлу в качестве своего первого аргумента: +Чтобы добавить вложения в электронное письмо, вы должны добавить вложения в массив, возвращаемый методом `attachments` почтового отправления. Во-первых, вы можете добавить вложение, указав путь к файлу в методе `fromPath` класса `Attachment`: + + use Illuminate\Mail\Mailables\Attachment; /** - * Создать сообщение. + * Получить вложения к сообщению. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attach('/path/to/file'); + return [ + Attachment::fromPath('/path/to/file'), + ]; } -При прикреплении файлов к сообщению вы также можете указать отображаемое имя и / или MIME-тип, передав массив в качестве второго аргумента методу `attach`: +При прикреплении файлов к сообщению вы также можете указать отображаемое имя и MIME-тип для вложения, используя методы `as` и `withMime`: /** - * Создать сообщение. + * Получить вложения к сообщению. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attach('/path/to/file', [ - 'as' => 'name.pdf', - 'mime' => 'application/pdf', - ]); + return [ + Attachment::fromPath('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf'), + ]; } #### Прикрепление файлов с диска -Если вы сохранили файл на одном из [дисков файлового хранилища](filesystem.md), то вы можете прикрепить его к электронному письму с помощью метода `attachFromStorage`: +Если вы сохранили файл на одном из [дисков файлового хранилища](filesystem.md), то вы можете прикрепить его к электронному письму с помощью метода `fromStorage` класса `Attachment`: /** - * Создать сообщение. + * Получить вложения к сообщению. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attachFromStorage('/path/to/file'); + return [ + Attachment::fromStorage('/path/to/file'), + ]; } -При необходимости вы можете указать имя вложения файла и дополнительные параметры, используя второй и третий аргументы метода `attachFromStorage`: +Конечно, вы также можете указать имя вложения и MIME-тип: /** - * Создать сообщение. + * Получить вложения к сообщению. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attachFromStorage('/path/to/file', 'name.pdf', [ - 'mime' => 'application/pdf' - ]); + return [ + Attachment::fromStorage('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf'), + ]; } -Метод `attachFromStorageDisk` используется, если вам нужно указать диск хранения, отличный от вашего диска по умолчанию: +Метод `fromStorage` используется, если вам нужно указать диск хранения, отличный от вашего диска по умолчанию: /** - * Создать сообщение. + * Получить вложения к сообщению. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attachFromStorageDisk('s3', '/path/to/file'); + return [ + Attachment::fromStorageDisk('s3', '/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf'), + ]; } #### Вложения необработанных данных -Метод `attachData` используется для присоединения необработанной строки в качестве вложения. Например, вы можете использовать этот метод, если вы создали PDF-файл в памяти и хотите прикрепить его к электронному письму, не записывая его на диск. Метод `attachData` принимает байты необработанных данных в качестве первого аргумента, имя файла в качестве второго аргумента и массив параметров в качестве третьего аргумента: +Метод `fromData` класса `Attachment` используется для прикрепления необработанной строки в качестве вложения. Например, вы можете использовать этот метод, если вы создали PDF-файл в памяти и хотите прикрепить его к электронному письму, не записывая его на диск. Метод `fromData` принимает замыкание, которое определяет байты необработанных данных, а также имя, которое должно быть присвоено вложению: /** - * Создать сообщение. + * Получить вложения к сообщению. * - * @return $this + * @return \Illuminate\Mail\Mailables\Attachment[] */ - public function build() + public function attachments() { - return $this->view('emails.orders.shipped') - ->attachData($this->pdf, 'name.pdf', [ - 'mime' => 'application/pdf', - ]); + return [ + Attachment::fromData(fn () => $this->pdf, 'Report.pdf') + ->withMime('application/pdf'), + ]; } @@ -456,7 +499,8 @@ php artisan make:mail OrderShipped ``` -> {note} Переменная `$message` недоступна в шаблонах текстовых сообщений, так как в текстовых сообщениях не используются встроенные вложения. +> **Предупреждение**\ +> Переменная `$message` недоступна в шаблонах текстовых сообщений, так как в текстовых сообщениях не используются встраиваемые вложения. #### Встраиваемые вложения необработанных данных @@ -471,21 +515,110 @@ php artisan make:mail OrderShipped ``` + +### Прикрепляемые объекты + +Хотя присоединения файлов к сообщениям с помощью простых строковых путей часто бывает достаточно, во многих случаях присоединяемые сущности в вашем приложении представлены классами. Например, если ваше приложение прикрепляет фотографию к сообщению, то ваше приложение также может иметь модель `Photo`, представляющую эту фотографию. Не было бы удобно просто передать модель `Photo` методу `attach`? Присоединяемые объекты позволяют сделать именно это. + +Для начала реализуйте интерфейс `Illuminate\Contracts\Mail\Attachable` для объекта, который будет прикрепляться к сообщениям. Этот интерфейс требует, чтобы ваш класс определял метод `toMailAttachment`, который возвращает экземпляр `Illuminate\Mail\Attachment`: + + photo]; + } + +Конечно, данные вложений могут храниться в удаленном файловом хранилище, таком как Amazon S3. Laravel также позволяет создавать экземпляры вложений из данных, которые хранятся на одном из [дисков файловой системы](filesystem.md) вашего приложения: + + // Создать вложение из файла на диске по умолчанию ... + return Attachment::fromStorage($this->path); + + // Создать вложение из файла на указанном диске... + return Attachment::fromStorageDisk('backblaze', $this->path); + +Кроме того, вы можете создавать экземпляры вложений с помощью данных, которые хранятся в памяти. Для этого передайте замыкание методу `fromData`. Замыкание должно возвращать необработанные данные, представляющие вложение: + + return Attachment::fromData(fn () => $this->content, 'Photo Name'); + +Laravel также предлагает дополнительные методы, которые вы можете использовать для настройки вложений. Например, вы можете использовать методы `as` и `withMime` для указания имени файла и MIME-типа: + + return Attachment::fromPath('/path/to/file') + ->as('Photo Name') + ->withMime('image/jpeg'); + + +### Заголовки + +Иногда требуется добавить к исходящему письму дополнительные заголовки. Например, вам может потребоваться установить собственный `Message-Id` или другие произвольные текстовые заголовки. + +Для этого определите метод `headers` для вашего почтового отправления. Метод `headers` должен возвращать экземпляр `Illuminate\Mail\Mailables\Headers`. Этот класс принимает параметры `messageId`, `references` и `text`. Конечно, вы можете указать только те параметры, которые вам нужны для вашего конкретного письма: + + use Illuminate\Mail\Mailables\Headers; + + /** + * Получить заголовки отправления. + * + * @return \Illuminate\Mail\Mailables\Headers + */ + public function headers() + { + return new Headers( + messageId: 'custom-message-id@example.com', + references: ['previous-message@example.com'], + text: [ + 'X-Custom-Header' => 'Custom Value', + ], + ); + } + ### Теги и метаданные -Некоторые сторонние почтовые поставщики, такие как Mailgun и Postmark, поддерживают «теги» и «метаданные» сообщения, которые могут использоваться для группировки и отслеживания электронных писем, отправляемых вашим приложением. Вы можете добавить теги и метаданные к сообщению электронной почты с помощью методов `tag` и `metadata`: +Некоторые сторонние почтовые поставщики, такие как Mailgun и Postmark, поддерживают «теги» и «метаданные» сообщения, которые могут использоваться для группировки и отслеживания электронных писем, отправляемых вашим приложением. Вы можете добавить теги и метаданные к сообщению электронной почты с помощью вашего определения `Envelope`: + + use Illuminate\Mail\Mailables\Envelope; /** - * Создать сообщение. + * Получить конверт сообщения. * - * @return $this + * @return \Illuminate\Mail\Mailables\Envelope */ - public function build() + public function envelope() { - return $this->view('emails.orders.shipped') - ->tag('shipment') - ->metadata('order_id', $this->order->id); + return new Envelope( + subject: 'Order Shipped', + tags: ['shipment'], + metadata: [ + 'order_id' => $this->order->id, + ], + ); } Если ваше приложение использует драйвер Mailgun, то вы можете обратиться к документации Mailgun для получения дополнительной информации о [тегах](https://documentation.mailgun.com/en/latest/user_manual.html#tagging-1) и [метаданных](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages). Кроме того, можно также обратиться к документации Postmark для получения дополнительной информации об их поддержке [тегов](https://postmarkapp.com/blog/tags-support-for-smtp) и [метаданных](https://postmarkapp.com/support/article/1125-custom-metadata-faq). @@ -495,26 +628,26 @@ php artisan make:mail OrderShipped ### Настройка сообщения Symfony -Метод `withSymfonyMessage` базового класса `Mailable` позволяет вам зарегистрировать замыкание, которое будет вызываться с экземпляром сообщения Symfony перед отправкой сообщения. Это дает вам возможность глубокой настройки сообщения перед его доставкой: +Почтовые возможности Laravel основаны на Symfony Mailer. Laravel позволяет вам регистрировать замыкания, которые будут вызываться экземпляром Symfony Message перед отправкой сообщения. Это дает вам возможность глубоко настроить сообщение перед его отправкой. Для этого определите параметр `using` в вашем определении `Envelope`: + use Illuminate\Mail\Mailables\Envelope; use Symfony\Component\Mime\Email; /** - * Создать сообщение. + * Получить конверт сообщения. * - * @return $this + * @return \Illuminate\Mail\Mailables\Envelope */ - public function build() + public function envelope() { - $this->view('emails.orders.shipped'); - - $this->withSymfonyMessage(function (Email $message) { - $message->getHeaders()->addTextHeader( - 'Custom-Header', 'Header Value' - ); - }); - - return $this; + return new Envelope( + subject: 'Order Shipped', + using: [ + function (Email $message) { + // ... + }, + ] + ); } @@ -531,19 +664,23 @@ php artisan make:mail OrderShipped php artisan make:mail OrderShipped --markdown=emails.orders.shipped ``` -Затем, в методе `build` почтового класса вызовите метод `markdown` вместо метода `view`. Метод `markdown` принимает имя шаблона Markdown и необязательный массив данных, которые должны быть доступны для шаблона: +Затем, в методе `content` при задании определения `Content` используйте параметр `markdown` вместо параметра `view`: + + use Illuminate\Mail\Mailables\Content; /** - * Создать сообщение. + * Получить определение содержимого сообщения. * - * @return $this + * @return \Illuminate\Mail\Mailables\Content */ - public function build() + public function content() { - return $this->from('example@example.com') - ->markdown('emails.orders.shipped', [ - 'url' => $this->orderUrl, - ]); + return new Content( + markdown: 'emails.orders.shipped', + with: [ + 'url' => $this->orderUrl, + ], + ); } @@ -552,21 +689,22 @@ php artisan make:mail OrderShipped --markdown=emails.orders.shipped Почтовые сообщения Markdown используют комбинацию компонентов Blade и синтаксиса Markdown, которые позволяют легко создавать почтовые сообщения, используя предварительно созданные компоненты пользовательского интерфейса электронной почты Laravel: ```blade -@component('mail::message') + # Order Shipped Your order has been shipped! -@component('mail::button', ['url' => $url]) + View Order -@endcomponent + Thanks,
{{ config('app.name') }} -@endcomponent +
``` -> {tip} Не используйте лишние отступы при написании писем Markdown. По стандартам Markdown парсеры будут отображать контент с отступом в виде блоков кода. +> **Примечание**\ +> Не используйте лишние отступы при написании писем Markdown. По стандартам Markdown парсеры будут отображать контент с отступом в виде блоков кода. #### Компонент Button @@ -574,9 +712,9 @@ Thanks,
Компонент кнопки отображает ссылку на кнопку по центру. Компонент принимает два аргумента: `url` и необязательный `color`. Поддерживаемые цвета: `primary`, `success` и `error`. Вы можете добавить к сообщению столько компонентов кнопки, сколько захотите: ```blade -@component('mail::button', ['url' => $url, 'color' => 'success']) + View Order -@endcomponent + ``` @@ -585,9 +723,9 @@ View Order Компонент панели отображает указанный блок текста на панели, цвет фона которой немного отличается от цвета остальной части сообщения. Это позволяет привлечь внимание к указанному блоку текста: ```blade -@component('mail::panel') + This is the panel content. -@endcomponent + ``` @@ -596,12 +734,12 @@ This is the panel content. Компонент таблицы позволяет преобразовать таблицу Markdown в таблицу HTML. Компонент принимает в качестве содержимого таблицу Markdown. Выравнивание столбцов таблицы поддерживается с использованием синтаксиса выравнивания таблицы Markdown по умолчанию: ```blade -@component('mail::table') + | Laravel | Table | Example | | ------------- |:-------------:| --------:| | Col 2 is | Centered | $10 | | Col 3 is | Right-Aligned | $20 | -@endcomponent + ``` @@ -770,7 +908,8 @@ php artisan vendor:publish --tag=laravel-mail } } -> {tip} Чтобы узнать больше о том, как обойти эти проблемы, просмотрите документацию, касающуюся [заданий в очереди и транзакций базы данных](queues.md#jobs-and-database-transactions). +> **Примечание**\ +> Чтобы узнать больше о том, как обойти эти проблемы, просмотрите документацию, касающуюся [заданий в очереди и транзакций базы данных](queues.md#jobs-and-database-transactions). ## Отрисовка отправлений @@ -795,7 +934,8 @@ php artisan vendor:publish --tag=laravel-mail return new App\Mail\InvoicePaid($invoice); }); -> {note} [Встраиваемые вложения](#inline-attachments) не будут отображаться при предварительном просмотре почтового сообщения в вашем браузере. Чтобы просмотреть эти почтовые сообщения, вы должны отправить их в приложение для тестирования электронной почты, например, [MailHog](https://github.com/mailhog/MailHog) или [HELO](https://usehelo.com). +> **Предупреждение**\ +> [Встраиваемые вложения](#inline-attachments) не будут отображаться при предварительном просмотре почтового сообщения в вашем браузере. Чтобы просмотреть эти почтовые сообщения, вы должны отправить их в приложение для тестирования электронной почты, например, [MailHog](https://github.com/mailhog/MailHog) или [HELO](https://usehelo.com). ## Локализация отправлений @@ -835,9 +975,9 @@ Laravel позволяет отправлять почтовые сообщен ## Тестирование отправлений -Laravel предлагает несколько удобных методов для тестирования того, что ваши почтовые сообщения содержат ожидаемый контент. Это следующие методы: `assertSeeInHtml`, `assertDontSeeInHtml`, `assertSeeInOrderInHtml`, `assertSeeInText`, `assertDontSeeInText` и `assertSeeInOrderInText`. +Laravel предлагает множество методов для проверки структуры вашего почтового отправления. Кроме того, Laravel предлагает несколько удобных методов для проверки того, содержит ли ваше почтовое отправление ожидаемый контент. Этими методами являются: `assertSeeInHtml`, `assertDontSeeInHtml`, `assertSeeInOrderInHtml`, `assertSeeInText`, `assertDontSeeInText`, `assertSeeInOrderInText`, `assertHasAttachment`, `assertHasAttachedData`, `assertHasAttachmentFromStorage` и `assertHasAttachmentFromStorageDisk`. -Как и следовало ожидать, утверждения «HTML» утверждают, что HTML-версия вашего почтового сообщения содержит переданную строку, в то время как утверждения «текст» утверждают, что текстовая версия вашего почтового сообщения содержит переданную строку: +Как и следовало ожидать, «HTML утверждения» необходимы для проверки того, что HTML-версия вашего почтового отправления содержит переданную строку, в то время как «текстовые утверждения» служат для проверки того, что текстовая версия вашего почтового отправления содержит переданную строку: use App\Mail\InvoicePaid; use App\Models\User; @@ -848,12 +988,27 @@ Laravel предлагает несколько удобных методов д $mailable = new InvoicePaid($user); + $mailable->assertFrom('jeffrey@example.com'); + $mailable->assertTo('taylor@example.com'); + $mailable->assertHasCc('abigail@example.com'); + $mailable->assertHasBcc('victoria@example.com'); + $mailable->assertHasReplyTo('tyler@example.com'); + $mailable->assertHasSubject('Invoice Paid'); + $mailable->assertHasTag('example-tag'); + $mailable->assertHasMetadata('key', 'value'); + $mailable->assertSeeInHtml($user->email); $mailable->assertSeeInHtml('Invoice Paid'); $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']); $mailable->assertSeeInText($user->email); $mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']); + + $mailable->assertHasAttachment('/path/to/file'); + $mailable->assertHasAttachment(Attachment::fromPath('/path/to/file')); + $mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); + $mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']); } @@ -902,17 +1057,23 @@ Laravel предлагает несколько удобных методов д Laravel запускает два события в процессе отправки почтовых сообщений. Событие `MessageSending` запускается перед отправкой сообщения, а событие `MessageSent` запускается после того, как сообщение было отправлено. Помните, что эти события запускаются, когда почта *отправляется*, а не когда она ставится в очередь. Как правило, регистрация слушателей этих событий осуществляется в поставщике `App\Providers\EventServiceProvider`: + use App\Listeners\LogSendingMessage; + use App\Listeners\LogSentMessage; + use Illuminate\Mail\Events\MessageSending; + use Illuminate\Mail\Events\MessageSent; + /** * Карта слушателей событий приложения. * * @var array */ protected $listen = [ - 'Illuminate\Mail\Events\MessageSending' => [ - 'App\Listeners\LogSendingMessage', + MessageSending::class => [ + LogSendingMessage::class, ], - 'Illuminate\Mail\Events\MessageSent' => [ - 'App\Listeners\LogSentMessage', + + MessageSent::class => [ + LogSentMessage::class, ], ]; @@ -943,6 +1104,8 @@ Laravel включает множество почтовых транспорт */ public function __construct(ApiClient $client) { + parent::__construct(); + $this->client = $client; } diff --git a/docs/middleware.md b/docs/middleware.md index 9bda77e..b86e7a1 100644 --- a/docs/middleware.md +++ b/docs/middleware.md @@ -57,7 +57,8 @@ php artisan make:middleware EnsureTokenIsValid Лучше всего представить себе посредников как серию «слоев» для HTTP-запроса, которые необходимо пройти, прежде чем запрос попадет в ваше приложение. Каждый слой может рассмотреть запрос и даже полностью отклонить его. -> {tip} Все посредники извлекаются из [контейнера служб](container.md), поэтому вы можете объявить необходимые вам зависимости в конструкторе посредника. +> **Примечание**\ +> Все посредники извлекаются из [контейнера служб](container.md), поэтому вы можете объявить необходимые вам зависимости в конструкторе посредника. @@ -182,7 +183,7 @@ php artisan make:middleware EnsureTokenIsValid По желанию можно сгруппировать несколько посредников под одним ключом, чтобы упростить их назначение маршрутам. Вы можете сделать это, используя свойство `$middlewareGroups` вашего HTTP-ядра. -По умолчанию Laravel поставляется с группами посредников `web` и `api`, которые содержат основных посредников, которые вы, возможно, захотите применить к своим веб- и API-маршрутам. Помните, что эти группы посредников автоматически применяются поставщиком служб `App\Providers\RouteServiceProvider` вашего приложения к маршрутам, определенным в файлах маршрутов `web` и `api`, соответственно: +Laravel включает предопределенные группы посредников `web` и `api`, которые содержат основных посредников, которые вы, возможно, захотите применить к своим веб- и API-маршрутам. Помните, что эти группы посредников автоматически применяются поставщиком служб `App\Providers\RouteServiceProvider` вашего приложения к маршрутам, определенным в файлах маршрутов `web` и `api`, соответственно: /** * Группы посредников маршрутов приложения. @@ -215,7 +216,8 @@ php artisan make:middleware EnsureTokenIsValid // }); -> {tip} Из коробки группы посредников `web` и `api` автоматически применяются к соответствующим файлам вашего приложения `routes/web.php` и `routes/api.php` с помощью `App\Providers\RouteServiceProvider`. +> **Примечание**\ +> Из коробки группы посредников `web` и `api` автоматически применяются к соответствующим файлам вашего приложения `routes/web.php` и `routes/api.php` с помощью `App\Providers\RouteServiceProvider`. ### Сортировка посредников @@ -230,6 +232,7 @@ php artisan make:middleware EnsureTokenIsValid * @var string[] */ protected $middlewarePriority = [ + \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, \Illuminate\Cookie\Middleware\EncryptCookies::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, diff --git a/docs/migrations.md b/docs/migrations.md index 2523752..17ba35f 100644 --- a/docs/migrations.md +++ b/docs/migrations.md @@ -15,6 +15,7 @@ - [Доступные типы столбцов](#available-column-types) - [Модификаторы столбца](#column-modifiers) - [Изменение столбцов](#modifying-columns) + - [Переименование столбцов](#renaming-columns) - [Удаление столбцов](#dropping-columns) - [Индексы](#indexes) - [Создание индексов](#creating-indexes) @@ -43,7 +44,8 @@ Laravel будет использовать имя миграции, чтобы Если вы хотите указать собственный путь для сгенерированной миграции, вы можете использовать параметр `--path` при выполнении команды `make:migration`. Указанный путь должен быть относительно базового пути вашего приложения. -> {tip} Заготовки миграции можно настроить с помощью [публикации заготовок](artisan.md#stub-customization). +> **Примечание**\ +> Заготовки миграции можно настроить с помощью [публикации заготовок](artisan.md#stub-customization). ### Сжатие миграций @@ -57,11 +59,19 @@ php artisan schema:dump php artisan schema:dump --prune ``` -Когда вы выполните эту команду, Laravel запишет файл «схемы» в каталог `database/schema` вашего приложения. Теперь, когда вы попытаетесь перенести свою базу данных, Laravel сначала выполнит SQL-операторы файла схемы, при условии, что никакие другие миграции не выполнялись. После выполнения команд файла схемы, Laravel выполнит все оставшиеся миграции, которые не были включены в дамп схемы БД. +Когда вы выполните эту команду, Laravel запишет файл «схемы» в каталог `database/schema` вашего приложения. Имя файла схемы будет соответствовать подключению к базе данных. Теперь, когда вы попытаетесь перенести свою базу данных, Laravel сначала выполнит SQL-операторы файла схемы, при условии, что никакие другие миграции не выполнялись. После выполнения команд файла схемы, Laravel выполнит все оставшиеся миграции, которые не были включены в дамп схемы БД. -Вы должны передать файл схемы базы данных в систему управления версиями, чтобы другие новые разработчики в вашей команде могли быстро воссоздать исходную структуру базы данных вашего приложения. +Если тесты вашего приложения используют соединение с базой данных, отличное от того, которое вы обычно используете во время локальной разработки, то вы должны убедиться, что вы создали дамп файла схемы, используя это соединение с базой данных, чтобы ваши тесты могли построить вашу базу данных. Вы можете сделать это после сброса подключения к базе данных, которое вы обычно используете во время локальной разработки: -> {note} Сжатие миграции доступно только для баз данных MySQL, PostgreSQL и SQLite и использует клиент командной строки базы данных. Дампы схемы не могут быть восстановлены в базах данных SQLite, хранимых в памяти. +```shell +php artisan schema:dump +php artisan schema:dump --database=testing --prune +``` + +Вы должны зафиксировать файл схемы базы данных в системе контроля версиями, чтобы другие / новые разработчики в вашей команде могли быстро воссоздать исходную структуру базы данных вашего приложения. + +> **Предупреждение**\ +> Сжатие миграции доступно только для баз данных MySQL, PostgreSQL и SQLite и использует клиент командной строки базы данных. Дампы схемы не могут быть восстановлены в базах данных SQLite, хранимых в памяти. ## Структура миграций @@ -141,6 +151,25 @@ php artisan migrate php artisan migrate:status ``` +Если вы хотите увидеть операции SQL, выполняемые при миграции, без их фактического запуска, то вы можете указать флаг `--pretend` для команды `migrate`: + +```shell +php artisan migrate --pretend +``` + +#### Изолирование выполнения миграции + +Если вы развертываете свое приложение на нескольких серверах и выполняете миграцию как часть процесса развертывания, то вы, вероятно, не хотите, чтобы два сервера пытались одновременно выполнить миграцию базу данных. Чтобы избежать этого, вы можете использовать флаг `isolated` при вызове команды `migrate`. + +При передачи флага `isolated`, Laravel получит атомарную блокировку, используя драйвер кеша вашего приложения, перед попыткой запуска миграций. Все другие попытки запустить команду `migrate`, пока эта блокировка удерживается, не будут выполнены; эта команда всегда завершается с кодом успешного выхода: + +```shell +php artisan migrate --isolated +``` + +> **Предупреждение**\ +> Чтобы использовать эту функцию, ваше приложение должно использовать драйвер кеша `memcached`, `redis`, `dynamodb`, `database`, `file` или `array` в качестве драйвера кеша вашего приложения по умолчанию. Кроме того, все серверы должны обмениваться данными с одним и тем же сервером центрального кэша. + #### Принудительный запуск миграции в рабочем окружении @@ -200,7 +229,8 @@ php artisan migrate:fresh php artisan migrate:fresh --seed ``` -> {note} Команда `migrate:fresh` удалит все таблицы базы данных независимо от их префикса. Эту команду следует использовать с осторожностью при разработке в базе данных, которая используется совместно с другими приложениями. +> **Предупреждение**\ +> Команда `migrate:fresh` удалит все таблицы базы данных независимо от их префикса. Эту команду следует использовать с осторожностью при разработке в базе данных, которая используется совместно с другими приложениями. ## Таблицы @@ -366,6 +396,7 @@ php artisan migrate:fresh --seed - [float](#column-method-float) - [foreignId](#column-method-foreignId) - [foreignIdFor](#column-method-foreignIdFor) +- [foreignUlid](#column-method-foreignUlid) - [foreignUuid](#column-method-foreignUuid) - [geometryCollection](#column-method-geometryCollection) - [geometry](#column-method-geometry) @@ -387,6 +418,7 @@ php artisan migrate:fresh --seed - [multiPolygon](#column-method-multiPolygon) - [nullableMorphs](#column-method-nullableMorphs) - [nullableTimestamps](#column-method-nullableTimestamps) +- [nullableUlidMorphs](#column-method-nullableUlidMorphs) - [nullableUuidMorphs](#column-method-nullableUuidMorphs) - [point](#column-method-point) - [polygon](#column-method-polygon) @@ -413,7 +445,9 @@ php artisan migrate:fresh --seed - [unsignedMediumInteger](#column-method-unsignedMediumInteger) - [unsignedSmallInteger](#column-method-unsignedSmallInteger) - [unsignedTinyInteger](#column-method-unsignedTinyInteger) +- [ulidMorphs](#column-method-ulidMorphs) - [uuidMorphs](#column-method-uuidMorphs) +- [ulid](#column-method-ulid) - [uuid](#column-method-uuid) - [year](#column-method-year) @@ -517,6 +551,13 @@ php artisan migrate:fresh --seed $table->foreignIdFor(User::class); + +#### `foreignUlid()` + +Метод `foreignUlid` создает эквивалент столбца `ULID`: + + $table->foreignUlid('user_id'); + #### `foreignUuid()` @@ -666,6 +707,13 @@ php artisan migrate:fresh --seed $table->nullableMorphs('taggable'); + +#### `nullableUlidMorphs()` + +Метод аналогичен методу [`ulidMorphs`](#column-method-ulidMorphs); тем не менее, создаваемый столбец будет иметь значение NULL: + + $table->nullableUlidMorphs('taggable'); + #### `nullableUuidMorphs()` @@ -848,6 +896,15 @@ php artisan migrate:fresh --seed $table->unsignedTinyInteger('votes'); + +#### `ulidMorphs()` + +Метод `ulidMorphs` – это удобный метод, который добавляет эквивалент столбца `CHAR(26)` (`{column}_id`) и эквивалент столбца `VARCHAR` (`{column}_type`). + +Этот метод предназначен для использования при определении столбцов, необходимых для полиморфного [отношения Eloquent](eloquent-relationships.md), использующего идентификаторы ULID. В следующем примере будут созданы столбцы `taggable_id` и `taggable_type`: + + $table->ulidMorphs('taggable'); + #### `uuidMorphs()` @@ -857,6 +914,13 @@ php artisan migrate:fresh --seed $table->uuidMorphs('taggable'); + +#### `ulid()` + +Метод `ulid` создает эквивалент столбца `ULID`: + + $table->ulid('id'); + #### `uuid()` @@ -935,7 +999,8 @@ php artisan migrate:fresh --seed } }; -> {note} Поддержка выражений по умолчанию зависит от вашего драйвера базы данных, версии базы данных и типа поля. См. документацию к вашей базе данных. Кроме того, невозможно комбинировать необработанные выражения `default` (используя `DB::raw`) и изменения столбцов через метод `change`. +> **Предупреждение**\ +> Поддержка выражений по умолчанию зависит от вашего драйвера базы данных, версии базы данных и типа поля. См. документацию к вашей базе данных. Кроме того, невозможно комбинировать необработанные выражения `default` (используя `DB::raw`) и изменения столбцов через метод `change`. #### Порядок столбцов @@ -970,7 +1035,8 @@ use Illuminate\Database\DBAL\TimestampType; ], ``` -> {note} Если ваше приложение использует Microsoft SQL Server, то убедитесь, что вы установили `doctrine/dbal:^3.0`. +> **Предупреждение**\ +> Если ваше приложение использует Microsoft SQL Server, то убедитесь, что вы установили `doctrine/dbal:^3.0`. #### Обновление атрибутов столбца @@ -987,23 +1053,35 @@ use Illuminate\Database\DBAL\TimestampType; $table->string('name', 50)->nullable()->change(); }); -> {note} Только следующие типы столбцов могут быть изменены: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger` и `uuid`. +> **Предупреждение**\ +> Только следующие типы столбцов могут быть изменены: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `double`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `tinyText`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger` и `uuid`. Чтобы изменить столбец типа `timestamp`, [должен быть зарегистрирован Doctrine Type](#prerequisites). -#### Переименование столбцов +### Переименование столбцов -Чтобы переименовать столбец, вы можете использовать метод `renameColumn` построителя схемы Blueprint. Перед переименованием столбца убедитесь, что вы установили библиотеку `doctrine/dbal` через менеджер пакетов Composer: +Чтобы переименовать столбец, вы можете использовать метод `renameColumn` построителя схемы: Schema::table('users', function (Blueprint $table) { $table->renameColumn('from', 'to'); }); -> {note} Переименование `enum` столбца в настоящее время не поддерживается. + +#### Переименование столбцов в устаревших базах данных + +Если вы используете установку базы данных более ранней, чем один из нижеуказанных выпусков, то вы должны убедиться, что вы установили библиотеку `doctrine/dbal` через менеджер пакетов Composer, прежде чем переименовывать столбец: + + + +- MySQL < `8.0.3` +- MariaDB < `10.5.2` +- SQLite < `3.25.0` + + ### Удаление столбцов -Чтобы удалить столбец, вы можете использовать метод `dropColumn` построителя схемы Blueprint. Если ваше приложение использует базу данных SQLite, то вы должны установить библиотеку `doctrine/dbal` через менеджер пакетов Composer, прежде чем использовать метод `dropColumn`: +Чтобы удалить столбец, вы можете использовать метод `dropColumn` построителя схемы: Schema::table('users', function (Blueprint $table) { $table->dropColumn('votes'); @@ -1015,7 +1093,11 @@ use Illuminate\Database\DBAL\TimestampType; $table->dropColumn(['votes', 'avatar', 'location']); }); -> {note} Удаление или изменение нескольких столбцов в рамках одной миграции при использовании базы данных SQLite не поддерживается. + + +#### Удаление столбцов в устаревших базах данных + +Если вы используете версию SQLite до `3.35.0`, то вы должны установить пакет `doctrine/dbal` через менеджер пакетов Composer, прежде чем можно будет использовать метод `dropColumn`. Удаление или изменение нескольких столбцов в рамках одной миграции при использовании этого пакета не поддерживается. #### Доступные псевдонимы команд @@ -1099,6 +1181,9 @@ Laravel содержит несколько удобных методов, св $table->renameIndex('from', 'to') +> **Предупреждение**\ +> Если ваше приложение использует базу данных SQLite, то вы должны установить пакет `doctrine/dbal` через менеджер пакетов Composer, прежде чем можно будет использовать метод `renameIndex`. + ### Удаление индексов @@ -1187,7 +1272,12 @@ Laravel также поддерживает создание ограничен Schema::disableForeignKeyConstraints(); -> {note} SQLite по умолчанию отключает ограничения внешнего ключа. При использовании SQLite убедитесь, что [включили поддержку внешнего ключа](database.md#configuration) в вашей конфигурации базы данных, прежде чем пытаться создать их в ваших миграциях. Кроме того, SQLite поддерживает внешние ключи только при создании, а [не при изменении таблиц](https://www.sqlite.org/omitted.html). + Schema::withoutForeignKeyConstraints(function () { + // Ограничения отключены в этом замыкании ... + }); + +> **Предупреждение**\ +> SQLite по умолчанию отключает ограничения внешнего ключа. При использовании SQLite убедитесь, что [включили поддержку внешнего ключа](database.md#configuration) в вашей конфигурации базы данных, прежде чем пытаться создать их в ваших миграциях. Кроме того, SQLite поддерживает внешние ключи только при создании, а [не при изменении таблиц](https://www.sqlite.org/omitted.html). ## События diff --git a/docs/mix-old.md b/docs/mix-old.md new file mode 100644 index 0000000..660fa47 --- /dev/null +++ b/docs/mix-old.md @@ -0,0 +1,434 @@ +# Laravel 9 · Компиляция веб-активов с помощью Mix + +- [Введение](#introduction) +- [Установка и настройка](#installation) +- [Запуск Mix](#running-mix) +- [Работа с таблицами стилей](#working-with-stylesheets) + - [Tailwind CSS](#tailwindcss) + - [PostCSS](#postcss) + - [Sass](#sass) + - [Обработка URL](#url-processing) + - [Карты исходников CSS](#css-source-maps) +- [Работа с JavaScript](#working-with-scripts) + - [Vue](#vue) + - [React](#react) + - [Извлечение сторонних библиотек](#vendor-extraction) + - [Пользовательская конфигурация Webpack](#custom-webpack-configuration) +- [Версионирование / очистка кеша](#versioning-and-cache-busting) +- [Перезагрузка с помощью Browsersync](#browsersync-reloading) +- [Переменные окружения](#environment-variables) +- [Уведомления](#notifications) + + +## Введение + +[Laravel Mix](https://github.com/laravel-mix/laravel-mix) – это пакет, разработанный создателем [Laracasts](https://laracasts.com) Джеффри Уэй, предлагает гибкий API для определения шагов сборки [Webpack](https://webpack.js.org) для вашего приложения с использованием нескольких распространенных препроцессоров CSS и JavaScript. + +Другими словами, Mix упрощает компиляцию и минимизацию файлов CSS и JavaScript вашего приложения. Посредством простой цепочки методов вы можете гибко определять свой сценарий по сборки веб-актива. Например: + +```js +mix.js('resources/js/app.js', 'public/js') + .postCss('resources/css/app.css', 'public/css'); +``` + +Если вы однажды были сбиты с толку и ошеломлены, начав работу с Webpack, то вам понравится Laravel Mix. Однако от вас не требуется использовать его при разработке приложения; вы можете использовать любой желаемый инструмент сборки, или даже не использовать его вовсе. Справедливо и обратное: вы можете использовать Laravel Mix без привязки вашего приложения к фреймворку Laravel. + +> {tip} Если вам нужно начать разработку приложения с помощью Laravel и [Tailwind CSS](https://tailwindcss.com), ознакомьтесь с одним из наших [стартовых комплектов приложения](starter-kits.md). + + +## Установка и настройка + + +#### Установка Node + +Перед запуском Mix вы должны сначала убедиться, что на вашем компьютере установлены Node.js и NPM: + +```shell +node -v +npm -v +``` + +Вы можете легко установить последнюю версию Node и NPM с помощью простых графических установщиков с [официального веб-сайта Node](https://nodejs.org/en/download/). Или, если вы используете [Laravel Sail](sail.md), вы можете вызывать Node и NPM через Sail: + +```shell +./vendor/bin/sail node -v +./vendor/bin/sail npm -v +``` + + +#### Установка Laravel Mix + +Единственный оставшийся шаг – установить Laravel Mix. В свежей установке Laravel вы найдете файл `package.json` в вашем корневом каталоге. Файл `package.json` по умолчанию уже включает в себя все, что вам нужно для начала работы с Laravel Mix. Думайте об этом файле как о вашем файле `composer.json`, за исключением того, что он определяет зависимости Node вместо зависимостей PHP. Вы можете установить зависимости, на которые он ссылается, запустив: + +```shell +npm install +``` + + +## Запуск Mix + +Mix – это слой конфигурации поверх [Webpack](https://webpack.js.org), поэтому для запуска задач Mix вам нужно только выполнить один из сценариев NPM, который содержится в файле `package.json` по умолчанию. Когда вы запускаете сценарии `dev` или `production`, все исходники активов CSS и JavaScript вашего приложения будут скомпилированы и помещены в каталог `public` приложения: + +```shell +// Запустить все задачи Mix ... +npm run dev + +// Запустить все задачи Mix и минифицировать на выходе ... +npm run prod +``` + + +#### Наблюдение за изменениями исходников веб-активов + +Команда `npm run watch` продолжит работу в консоли и будет следить за изменениями во всех соответствующих файлах CSS и JavaScript. Webpack автоматически перекомпилирует ваши исходники, когда обнаружит изменение в одном из этих файлов: + +```shell +npm run watch +``` + +Webpack может не обнаруживать изменения ваших файлов в определенных локальных средах разработки. Если это наблюдается в вашей системе, рассмотрите возможность использования команды `watch-poll`: + +```shell +npm run watch-poll +``` + + +## Работа с таблицами стилей + +Файл `webpack.mix.js` вашего приложения является отправной точкой для компиляции всех веб-активов. Думайте об этом как о легкой конфигурационной обертке вокруг [Webpack](https://webpack.js.org). Задачи Mix можно объединить в цепочку при определении того, как должны быть скомпилированы ваши веб-активы. + + +### Tailwind CSS + +[Tailwind CSS](https://tailwindcss.com) – это современный, низкоутилитарный фреймворк для создания удивительных сайтов, не покидая HTML-разметку. Давайте рассмотрим, как начать использовать его в проекте Laravel совместно с Mix. Во-первых, мы должны установить Tailwind с помощью NPM и сгенерировать наш конфигурационный файл Tailwind: + +```shell +npm install + +npm install -D tailwindcss + +npx tailwindcss init +``` + +Команда `init` сгенерирует файл `tailwind.config.js`. Раздел `content` этого файла позволяет вам настроить пути ко всем вашим шаблонам HTML, компонентам JavaScript и любым другим исходным файлам, которые содержат имена классов Tailwind, чтобы все классы CSS, не используемые в этих файлах, были удалены из вашего конечного файла CSS: + +```js +content: [ + './storage/framework/views/*.php', + './resources/**/*.blade.php', + './resources/**/*.js', + './resources/**/*.vue', + './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', +], +``` + +Затем вы должны добавить каждый из «слоев» Tailwind в файл `resources/css/app.css` вашего приложения: + +```css +@tailwind base; +@tailwind components; +@tailwind utilities; +``` + +После того, как вы указали необходимые слои Tailwind, необходимо обновить файл `webpack.mix.js` вашего приложения, чтобы скомпилировать ваш CSS, использующий Tailwind: + +```js +mix.js('resources/js/app.js', 'public/js') + .postCss('resources/css/app.css', 'public/css', [ + require('tailwindcss'), + ]); +``` + +Наконец, вы должны указать свою таблицу стилей в основном шаблоне макета вашего приложения. Многие разработчики приложений предпочитают хранить этот шаблон в `resources/views/layouts/app.blade.php`. Кроме того, убедитесь, что вы добавили метатег `viewport`, если он еще не присутствует: + +```blade + + + + + +``` + + +### PostCSS + +[PostCSS](https://postcss.org/) – мощный инструмент для преобразования вашего CSS, включен в Laravel Mix из коробки. По умолчанию Mix использует популярный плагин [Autoprefixer](https://github.com/postcss/autoprefixer) для автоматического применения всех необходимых префиксов CSS3. Однако вы можете добавлять любые дополнительные плагины, подходящие для вашего приложения. + +Сначала установите нужный плагин через NPM и включите его в свой массив плагинов при вызове метода `postCss` Mix. Метод `postCss` принимает путь к вашему файлу CSS в качестве первого аргумента, а в качестве второго аргумента – каталог, в который следует поместить скомпилированный файл: + +```js +mix.postCss('resources/css/app.css', 'public/css', [ + require('postcss-custom-properties') +]); +``` + +Или вы можете выполнить `postCss` без дополнительных плагинов, чтобы получить простую компиляцию и минификацию CSS: + +```js +mix.postCss('resources/css/app.css', 'public/css'); +``` + + +### Sass + +Метод `sass` позволяет вам скомпилировать [Sass](https://sass-lang.com/) в CSS, понятный веб-браузерам. Метод `sass` принимает путь к вашему файлу Sass в качестве своего первого аргумента и каталог, в который должен быть помещен скомпилированный файл, в качестве второго аргумента: + +```js +mix.sass('resources/sass/app.scss', 'public/css'); +``` + +Вы можете скомпилировать несколько файлов Sass в отдельные файлы CSS и даже настроить каталог назначения результирующего CSS, вызывая метод `sass` несколько раз: + +```js +mix.sass('resources/sass/app.sass', 'public/css') + .sass('resources/sass/admin.sass', 'public/css/admin'); +``` + + +### Обработка URL + +Поскольку Laravel Mix построен поверх Webpack, важно понимать несколько концепций Webpack. Для компиляции CSS Webpack перезапишет и оптимизирует любые вызовы `url()` в ваших таблицах стилей. Хотя поначалу это может показаться странным, это невероятно мощная функциональность. Представьте, что мы хотим скомпилировать Sass, который включает относительный URL-адрес изображения: + +```css +.example { + background: url('../images/example.png'); +} +``` + +> {note} Абсолютные пути для любого заданного `url()` будут исключены из перезаписи URL. Например, `url('/images/thing.png')` или `url('http://example.com/images/thing.png')` не будут изменены. + +По умолчанию Laravel Mix и Webpack найдут `example.png`, скопируют его в вашу папку `public/images`, а затем перепишут `url()` в созданной вами таблице стилей. Таким образом, ваш скомпилированный CSS будет: + +```css +.example { + background: url(/images/example.png?d41d8cd98f00b204e9800998ecf8427e); +} +``` + +Какой бы полезной ни была эта функция, возможно, что ваша существующая структура папок уже настроена так, как вам нравится. В этом случае вы можете отключить перезапись `url()` следующим образом: + +```js +mix.sass('resources/sass/app.scss', 'public/css').options({ + processCssUrls: false +}); +``` + +После добавления этой записи в ваш файл `webpack.mix.js`, Mix больше не будет сопоставлять какой-либо `url()` или копировать веб-активы в ваш публичный каталог. Другими словами, скомпилированный CSS будет выглядеть так же, как вы его изначально указали: + +```css +.example { + background: url("../images/thing.png"); +} +``` + + +### Карты исходников CSS + +Хотя по умолчанию они отключены, карты исходников могут быть активированы путем вызова метода `mix.sourceMaps()` в вашем файле `webpack.mix.js`. Хоть это и связано с затратами на компиляцию / производительность, но, в свою очередь, предоставит дополнительную отладочную информацию в инструментах разработчика вашего браузера при использовании скомпилированных веб-активов: + +```js +mix.js('resources/js/app.js', 'public/js') + .sourceMaps(); +``` + + +#### Стиль сопоставления исходников + +Webpack предлагает множество [стилей сопоставления исходников](https://webpack.js.org/configuration/devtool/#devtool). По умолчанию стиль сопоставления исходников Mix установлен как `eval-source-map`, что обеспечивает быстрое время перестроения. Если вы хотите изменить стиль сопоставления, то вы можете сделать это с помощью метода `sourceMaps`: + +```js +let productionSourceMaps = false; + +mix.js('resources/js/app.js', 'public/js') + .sourceMaps(productionSourceMaps, 'source-map'); +``` + + +## Работа с JavaScript + +Mix содержит несколько функций, которые помогут вам работать с вашими файлами JavaScript, например, компиляция современного ECMAScript, объединение модулей, минификация и объединение простых файлов JavaScript. Более того, все это работает без проблем, не требуя ни унции специального конфигурирования: + +```js +mix.js('resources/js/app.js', 'public/js'); +``` + +Теперь, с помощью одной строчки кода вы можете воспользоваться следующими преимуществами: + + + +- синтаксис современного EcmaScript. +- модули +- минификация для эксплуатационного режима. + + + + +### Vue + +Mix автоматически установит плагины Babel, необходимые для поддержки компиляции однофайловых компонентов Vue при использовании метода `vue`. Никакой дополнительной настройки не требуется: + +```js +mix.js('resources/js/app.js', 'public/js') + .vue(); +``` + +После того, как ваш JavaScript скомпилирован, вы можете ссылаться на него в своем приложении: + +```blade + + + + + +``` + + +### React + +Mix автоматически установит плагины Babel, необходимые для поддержки React. Для начала добавьте вызов метода `react`: + +```js +mix.js('resources/js/app.jsx', 'public/js') + .react(); +``` + +За кулисами Mix загрузит и включит соответствующий плагин `babel-preset-react` Babel. После того, как ваш JavaScript скомпилирован, вы можете ссылаться на него в своем приложении: + +```blade + + + + + +``` + + +### Извлечение сторонних библиотек + +Одним из потенциальных недостатков объединения всего кода JavaScript для конкретного приложения со сторонними библиотеками, такими как React и Vue, является то, что это затрудняет долгосрочное кеширование. Например, одно обновление кода вашего приложения заставит браузер повторно загрузить все сторонние библиотеки, даже если они не изменились. + +Если вы намереваетесь часто обновлять JavaScript своего приложения, вам следует рассмотреть возможность извлечения всех сторонних библиотек в их собственный файл. Таким образом, изменение кода вашего приложения не повлияет на кеширование вашего большого файла `vendor.js`. Метод `extract` Mix делает это проще простого: + +```js +mix.js('resources/js/app.js', 'public/js') + .extract(['vue']) +``` + +Метод `extract` принимает массив всех библиотек или модулей, которые вы хотите извлечь в файл `vendor.js`. Используя приведенный выше фрагмент в качестве примера, Mix сгенерирует следующие файлы: + + + +- `public/js/manifest.js`: *The Webpack manifest runtime* +- `public/js/vendor.js`: *Your vendor libraries* +- `public/js/app.js`: *Your application code* + + + +Чтобы избежать ошибок JavaScript, обязательно загружайте эти файлы в правильном порядке: + +```html + + + +``` + + +### Пользовательская конфигурация Webpack + +Иногда требуется дополнительные изменения базовой конфигурации Webpack. Например, у вас может быть специальный загрузчик или плагин, на который нужно сослаться. + +Mix содержит полезный метод `webpackConfig`, который позволяет вам объединить небольшие переопределения конфигурации Webpack. Это особенно привлекательный вариант, поскольку он не требует от вас копирования и поддержки вашей собственной копии файла `webpack.config.js`. Метод `webpackConfig` принимает объект, который должен содержать любую [специфичную для Webpack конфигурацию](https://webpack.js.org/configuration/), которую вы хотите применить. + +```js +mix.webpackConfig({ + resolve: { + modules: [ + path.resolve(__dirname, 'vendor/laravel/spark/resources/assets/js') + ] + } +}); +``` + + +## Версионирование / очистка кеша + +Многие разработчики дополняют имена своих скомпилированных веб-активов меткой времени или уникальным токеном, чтобы заставить браузеры загружать свежие активы вместо устаревших копий кода. Mix может автоматически обработать это за вас, используя метод `version`. + +Метод `version` добавит уникальный хеш к именам файлов всех скомпилированных исходников, что сделает очистку кеша более удобной: + +```js +mix.js('resources/js/app.js', 'public/js') + .version(); +``` + +После создания файла версионирования вы не узнаете его точное имя. Итак, вы должны использовать глобальную функцию `mix` Laravel в вашем [шаблоне](views.md) для загрузки хешированного веб-актива. Глобальная функция `mix` фреймворка Laravel автоматически определит текущее имя хешированного файла: + +```blade + +``` + +Поскольку файлы с поддержкой версий обычно нужны только в эксплуатационном окружении, вы можете указать, чтобы процесс управления версиями выполнялся только во время запуска `npm run prod`: + +```js +mix.js('resources/js/app.js', 'public/js'); + +if (mix.inProduction()) { + mix.version(); +} +``` + + +#### Изменение базовых URL-адресов с помощью Mix + +Если ваши скомпилированные веб-активы Mix развернуты отдельно от вашего приложения в CDN, то вам нужно будет изменить базовый URL-адрес, генерируемый функцией `mix`. Вы можете сделать это, добавив параметр `mix_url` в конфигурационный файл `config/app.php` вашего приложения: + + 'mix_url' => env('MIX_ASSET_URL', null) + +После указания URL-адреса, функция `mix` будет подставлять указанный префикс при создании URL-адресов для веб-активов: + +```shell +https://cdn.example.com/js/app.js?id=1964becbdd96414518cd +``` + + +## Перезагрузка с помощью Browsersync + +[BrowserSync](https://browsersync.io/) может автоматически отслеживать изменения в ваших файлах и вносить изменения в браузер, не требуя обновления страницы вручную. Вы можете включить эту поддержку, вызвав метод `mix.browserSync()`: + +```js +mix.browserSync('laravel.test'); +``` + +[Параметры BrowserSync](https://browsersync.io/docs/options) можно указать путем передачи объекта JavaScript в метод `browserSync`: + +```js +mix.browserSync({ + proxy: 'laravel.test' +}); +``` + +Затем запустите сервер разработки Webpack с помощью команды `npm run watch`. Теперь, когда вы изменяете скрипт или файл PHP, вы можете наблюдать, как браузер мгновенно обновляет страницу, чтобы отразить ваши изменения. + + +## Переменные окружения + +Вы можете использовать переменные окружения в своем `webpack.mix.js`, добавив к одной из переменных префикс `MIX_` в вашем файле `.env`: + +```ini +MIX_SENTRY_DSN_PUBLIC=http://example.com +``` + +После того, как переменная была определена в вашем файле `.env`, вы можете получить к ней доступ через объект `process.env`. Однако, вам нужно будет перезапустить задание, если значение переменной среды изменится во время ее выполнения: + +```js +process.env.MIX_SENTRY_DSN_PUBLIC +``` + + +## Уведомления + +Когда доступно, Mix будет автоматически отображать уведомления ОС при компиляции, давая вам мгновенную информацию о том, была ли компиляция успешной или нет. Однако, могут быть случаи, когда вы предпочтете отключить эти уведомления. Одним из таких примеров может быть запуск Mix на вашем рабочем сервере. Уведомления можно отключить с помощью метода `disableNotifications`: + +```js +mix.disableNotifications(); +``` diff --git a/docs/mix.md b/docs/mix.md index 660fa47..e84df66 100644 --- a/docs/mix.md +++ b/docs/mix.md @@ -1,23 +1,6 @@ -# Laravel 9 · Компиляция веб-активов с помощью Mix +# Laravel 9 · Пакет Laravel Mix - [Введение](#introduction) -- [Установка и настройка](#installation) -- [Запуск Mix](#running-mix) -- [Работа с таблицами стилей](#working-with-stylesheets) - - [Tailwind CSS](#tailwindcss) - - [PostCSS](#postcss) - - [Sass](#sass) - - [Обработка URL](#url-processing) - - [Карты исходников CSS](#css-source-maps) -- [Работа с JavaScript](#working-with-scripts) - - [Vue](#vue) - - [React](#react) - - [Извлечение сторонних библиотек](#vendor-extraction) - - [Пользовательская конфигурация Webpack](#custom-webpack-configuration) -- [Версионирование / очистка кеша](#versioning-and-cache-busting) -- [Перезагрузка с помощью Browsersync](#browsersync-reloading) -- [Переменные окружения](#environment-variables) -- [Уведомления](#notifications) ## Введение @@ -33,402 +16,8 @@ mix.js('resources/js/app.js', 'public/js') Если вы однажды были сбиты с толку и ошеломлены, начав работу с Webpack, то вам понравится Laravel Mix. Однако от вас не требуется использовать его при разработке приложения; вы можете использовать любой желаемый инструмент сборки, или даже не использовать его вовсе. Справедливо и обратное: вы можете использовать Laravel Mix без привязки вашего приложения к фреймворку Laravel. -> {tip} Если вам нужно начать разработку приложения с помощью Laravel и [Tailwind CSS](https://tailwindcss.com), ознакомьтесь с одним из наших [стартовых комплектов приложения](starter-kits.md). +> **Примечание**\ +> Vite заменил Laravel Mix в новых установках Laravel. Документацию по Mix можно найти на [официальном сайте Laravel Mix](https://laravel-mix.com/). Если вы хотите переключиться на Vite, ознакомьтесь с нашим [руководством по миграции на Vite](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). - -## Установка и настройка - - -#### Установка Node - -Перед запуском Mix вы должны сначала убедиться, что на вашем компьютере установлены Node.js и NPM: - -```shell -node -v -npm -v -``` - -Вы можете легко установить последнюю версию Node и NPM с помощью простых графических установщиков с [официального веб-сайта Node](https://nodejs.org/en/download/). Или, если вы используете [Laravel Sail](sail.md), вы можете вызывать Node и NPM через Sail: - -```shell -./vendor/bin/sail node -v -./vendor/bin/sail npm -v -``` - - -#### Установка Laravel Mix - -Единственный оставшийся шаг – установить Laravel Mix. В свежей установке Laravel вы найдете файл `package.json` в вашем корневом каталоге. Файл `package.json` по умолчанию уже включает в себя все, что вам нужно для начала работы с Laravel Mix. Думайте об этом файле как о вашем файле `composer.json`, за исключением того, что он определяет зависимости Node вместо зависимостей PHP. Вы можете установить зависимости, на которые он ссылается, запустив: - -```shell -npm install -``` - - -## Запуск Mix - -Mix – это слой конфигурации поверх [Webpack](https://webpack.js.org), поэтому для запуска задач Mix вам нужно только выполнить один из сценариев NPM, который содержится в файле `package.json` по умолчанию. Когда вы запускаете сценарии `dev` или `production`, все исходники активов CSS и JavaScript вашего приложения будут скомпилированы и помещены в каталог `public` приложения: - -```shell -// Запустить все задачи Mix ... -npm run dev - -// Запустить все задачи Mix и минифицировать на выходе ... -npm run prod -``` - - -#### Наблюдение за изменениями исходников веб-активов - -Команда `npm run watch` продолжит работу в консоли и будет следить за изменениями во всех соответствующих файлах CSS и JavaScript. Webpack автоматически перекомпилирует ваши исходники, когда обнаружит изменение в одном из этих файлов: - -```shell -npm run watch -``` - -Webpack может не обнаруживать изменения ваших файлов в определенных локальных средах разработки. Если это наблюдается в вашей системе, рассмотрите возможность использования команды `watch-poll`: - -```shell -npm run watch-poll -``` - - -## Работа с таблицами стилей - -Файл `webpack.mix.js` вашего приложения является отправной точкой для компиляции всех веб-активов. Думайте об этом как о легкой конфигурационной обертке вокруг [Webpack](https://webpack.js.org). Задачи Mix можно объединить в цепочку при определении того, как должны быть скомпилированы ваши веб-активы. - - -### Tailwind CSS - -[Tailwind CSS](https://tailwindcss.com) – это современный, низкоутилитарный фреймворк для создания удивительных сайтов, не покидая HTML-разметку. Давайте рассмотрим, как начать использовать его в проекте Laravel совместно с Mix. Во-первых, мы должны установить Tailwind с помощью NPM и сгенерировать наш конфигурационный файл Tailwind: - -```shell -npm install - -npm install -D tailwindcss - -npx tailwindcss init -``` - -Команда `init` сгенерирует файл `tailwind.config.js`. Раздел `content` этого файла позволяет вам настроить пути ко всем вашим шаблонам HTML, компонентам JavaScript и любым другим исходным файлам, которые содержат имена классов Tailwind, чтобы все классы CSS, не используемые в этих файлах, были удалены из вашего конечного файла CSS: - -```js -content: [ - './storage/framework/views/*.php', - './resources/**/*.blade.php', - './resources/**/*.js', - './resources/**/*.vue', - './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php', -], -``` - -Затем вы должны добавить каждый из «слоев» Tailwind в файл `resources/css/app.css` вашего приложения: - -```css -@tailwind base; -@tailwind components; -@tailwind utilities; -``` - -После того, как вы указали необходимые слои Tailwind, необходимо обновить файл `webpack.mix.js` вашего приложения, чтобы скомпилировать ваш CSS, использующий Tailwind: - -```js -mix.js('resources/js/app.js', 'public/js') - .postCss('resources/css/app.css', 'public/css', [ - require('tailwindcss'), - ]); -``` - -Наконец, вы должны указать свою таблицу стилей в основном шаблоне макета вашего приложения. Многие разработчики приложений предпочитают хранить этот шаблон в `resources/views/layouts/app.blade.php`. Кроме того, убедитесь, что вы добавили метатег `viewport`, если он еще не присутствует: - -```blade - - - - - -``` - - -### PostCSS - -[PostCSS](https://postcss.org/) – мощный инструмент для преобразования вашего CSS, включен в Laravel Mix из коробки. По умолчанию Mix использует популярный плагин [Autoprefixer](https://github.com/postcss/autoprefixer) для автоматического применения всех необходимых префиксов CSS3. Однако вы можете добавлять любые дополнительные плагины, подходящие для вашего приложения. - -Сначала установите нужный плагин через NPM и включите его в свой массив плагинов при вызове метода `postCss` Mix. Метод `postCss` принимает путь к вашему файлу CSS в качестве первого аргумента, а в качестве второго аргумента – каталог, в который следует поместить скомпилированный файл: - -```js -mix.postCss('resources/css/app.css', 'public/css', [ - require('postcss-custom-properties') -]); -``` - -Или вы можете выполнить `postCss` без дополнительных плагинов, чтобы получить простую компиляцию и минификацию CSS: - -```js -mix.postCss('resources/css/app.css', 'public/css'); -``` - - -### Sass - -Метод `sass` позволяет вам скомпилировать [Sass](https://sass-lang.com/) в CSS, понятный веб-браузерам. Метод `sass` принимает путь к вашему файлу Sass в качестве своего первого аргумента и каталог, в который должен быть помещен скомпилированный файл, в качестве второго аргумента: - -```js -mix.sass('resources/sass/app.scss', 'public/css'); -``` - -Вы можете скомпилировать несколько файлов Sass в отдельные файлы CSS и даже настроить каталог назначения результирующего CSS, вызывая метод `sass` несколько раз: - -```js -mix.sass('resources/sass/app.sass', 'public/css') - .sass('resources/sass/admin.sass', 'public/css/admin'); -``` - - -### Обработка URL - -Поскольку Laravel Mix построен поверх Webpack, важно понимать несколько концепций Webpack. Для компиляции CSS Webpack перезапишет и оптимизирует любые вызовы `url()` в ваших таблицах стилей. Хотя поначалу это может показаться странным, это невероятно мощная функциональность. Представьте, что мы хотим скомпилировать Sass, который включает относительный URL-адрес изображения: - -```css -.example { - background: url('../images/example.png'); -} -``` - -> {note} Абсолютные пути для любого заданного `url()` будут исключены из перезаписи URL. Например, `url('/images/thing.png')` или `url('http://example.com/images/thing.png')` не будут изменены. - -По умолчанию Laravel Mix и Webpack найдут `example.png`, скопируют его в вашу папку `public/images`, а затем перепишут `url()` в созданной вами таблице стилей. Таким образом, ваш скомпилированный CSS будет: - -```css -.example { - background: url(/images/example.png?d41d8cd98f00b204e9800998ecf8427e); -} -``` - -Какой бы полезной ни была эта функция, возможно, что ваша существующая структура папок уже настроена так, как вам нравится. В этом случае вы можете отключить перезапись `url()` следующим образом: - -```js -mix.sass('resources/sass/app.scss', 'public/css').options({ - processCssUrls: false -}); -``` - -После добавления этой записи в ваш файл `webpack.mix.js`, Mix больше не будет сопоставлять какой-либо `url()` или копировать веб-активы в ваш публичный каталог. Другими словами, скомпилированный CSS будет выглядеть так же, как вы его изначально указали: - -```css -.example { - background: url("../images/thing.png"); -} -``` - - -### Карты исходников CSS - -Хотя по умолчанию они отключены, карты исходников могут быть активированы путем вызова метода `mix.sourceMaps()` в вашем файле `webpack.mix.js`. Хоть это и связано с затратами на компиляцию / производительность, но, в свою очередь, предоставит дополнительную отладочную информацию в инструментах разработчика вашего браузера при использовании скомпилированных веб-активов: - -```js -mix.js('resources/js/app.js', 'public/js') - .sourceMaps(); -``` - - -#### Стиль сопоставления исходников - -Webpack предлагает множество [стилей сопоставления исходников](https://webpack.js.org/configuration/devtool/#devtool). По умолчанию стиль сопоставления исходников Mix установлен как `eval-source-map`, что обеспечивает быстрое время перестроения. Если вы хотите изменить стиль сопоставления, то вы можете сделать это с помощью метода `sourceMaps`: - -```js -let productionSourceMaps = false; - -mix.js('resources/js/app.js', 'public/js') - .sourceMaps(productionSourceMaps, 'source-map'); -``` - - -## Работа с JavaScript - -Mix содержит несколько функций, которые помогут вам работать с вашими файлами JavaScript, например, компиляция современного ECMAScript, объединение модулей, минификация и объединение простых файлов JavaScript. Более того, все это работает без проблем, не требуя ни унции специального конфигурирования: - -```js -mix.js('resources/js/app.js', 'public/js'); -``` - -Теперь, с помощью одной строчки кода вы можете воспользоваться следующими преимуществами: - - - -- синтаксис современного EcmaScript. -- модули -- минификация для эксплуатационного режима. - - - - -### Vue - -Mix автоматически установит плагины Babel, необходимые для поддержки компиляции однофайловых компонентов Vue при использовании метода `vue`. Никакой дополнительной настройки не требуется: - -```js -mix.js('resources/js/app.js', 'public/js') - .vue(); -``` - -После того, как ваш JavaScript скомпилирован, вы можете ссылаться на него в своем приложении: - -```blade - - - - - -``` - - -### React - -Mix автоматически установит плагины Babel, необходимые для поддержки React. Для начала добавьте вызов метода `react`: - -```js -mix.js('resources/js/app.jsx', 'public/js') - .react(); -``` - -За кулисами Mix загрузит и включит соответствующий плагин `babel-preset-react` Babel. После того, как ваш JavaScript скомпилирован, вы можете ссылаться на него в своем приложении: - -```blade - - - - - -``` - - -### Извлечение сторонних библиотек - -Одним из потенциальных недостатков объединения всего кода JavaScript для конкретного приложения со сторонними библиотеками, такими как React и Vue, является то, что это затрудняет долгосрочное кеширование. Например, одно обновление кода вашего приложения заставит браузер повторно загрузить все сторонние библиотеки, даже если они не изменились. - -Если вы намереваетесь часто обновлять JavaScript своего приложения, вам следует рассмотреть возможность извлечения всех сторонних библиотек в их собственный файл. Таким образом, изменение кода вашего приложения не повлияет на кеширование вашего большого файла `vendor.js`. Метод `extract` Mix делает это проще простого: - -```js -mix.js('resources/js/app.js', 'public/js') - .extract(['vue']) -``` - -Метод `extract` принимает массив всех библиотек или модулей, которые вы хотите извлечь в файл `vendor.js`. Используя приведенный выше фрагмент в качестве примера, Mix сгенерирует следующие файлы: - - - -- `public/js/manifest.js`: *The Webpack manifest runtime* -- `public/js/vendor.js`: *Your vendor libraries* -- `public/js/app.js`: *Your application code* - - - -Чтобы избежать ошибок JavaScript, обязательно загружайте эти файлы в правильном порядке: - -```html - - - -``` - - -### Пользовательская конфигурация Webpack - -Иногда требуется дополнительные изменения базовой конфигурации Webpack. Например, у вас может быть специальный загрузчик или плагин, на который нужно сослаться. - -Mix содержит полезный метод `webpackConfig`, который позволяет вам объединить небольшие переопределения конфигурации Webpack. Это особенно привлекательный вариант, поскольку он не требует от вас копирования и поддержки вашей собственной копии файла `webpack.config.js`. Метод `webpackConfig` принимает объект, который должен содержать любую [специфичную для Webpack конфигурацию](https://webpack.js.org/configuration/), которую вы хотите применить. - -```js -mix.webpackConfig({ - resolve: { - modules: [ - path.resolve(__dirname, 'vendor/laravel/spark/resources/assets/js') - ] - } -}); -``` - - -## Версионирование / очистка кеша - -Многие разработчики дополняют имена своих скомпилированных веб-активов меткой времени или уникальным токеном, чтобы заставить браузеры загружать свежие активы вместо устаревших копий кода. Mix может автоматически обработать это за вас, используя метод `version`. - -Метод `version` добавит уникальный хеш к именам файлов всех скомпилированных исходников, что сделает очистку кеша более удобной: - -```js -mix.js('resources/js/app.js', 'public/js') - .version(); -``` - -После создания файла версионирования вы не узнаете его точное имя. Итак, вы должны использовать глобальную функцию `mix` Laravel в вашем [шаблоне](views.md) для загрузки хешированного веб-актива. Глобальная функция `mix` фреймворка Laravel автоматически определит текущее имя хешированного файла: - -```blade - -``` - -Поскольку файлы с поддержкой версий обычно нужны только в эксплуатационном окружении, вы можете указать, чтобы процесс управления версиями выполнялся только во время запуска `npm run prod`: - -```js -mix.js('resources/js/app.js', 'public/js'); - -if (mix.inProduction()) { - mix.version(); -} -``` - - -#### Изменение базовых URL-адресов с помощью Mix - -Если ваши скомпилированные веб-активы Mix развернуты отдельно от вашего приложения в CDN, то вам нужно будет изменить базовый URL-адрес, генерируемый функцией `mix`. Вы можете сделать это, добавив параметр `mix_url` в конфигурационный файл `config/app.php` вашего приложения: - - 'mix_url' => env('MIX_ASSET_URL', null) - -После указания URL-адреса, функция `mix` будет подставлять указанный префикс при создании URL-адресов для веб-активов: - -```shell -https://cdn.example.com/js/app.js?id=1964becbdd96414518cd -``` - - -## Перезагрузка с помощью Browsersync - -[BrowserSync](https://browsersync.io/) может автоматически отслеживать изменения в ваших файлах и вносить изменения в браузер, не требуя обновления страницы вручную. Вы можете включить эту поддержку, вызвав метод `mix.browserSync()`: - -```js -mix.browserSync('laravel.test'); -``` - -[Параметры BrowserSync](https://browsersync.io/docs/options) можно указать путем передачи объекта JavaScript в метод `browserSync`: - -```js -mix.browserSync({ - proxy: 'laravel.test' -}); -``` - -Затем запустите сервер разработки Webpack с помощью команды `npm run watch`. Теперь, когда вы изменяете скрипт или файл PHP, вы можете наблюдать, как браузер мгновенно обновляет страницу, чтобы отразить ваши изменения. - - -## Переменные окружения - -Вы можете использовать переменные окружения в своем `webpack.mix.js`, добавив к одной из переменных префикс `MIX_` в вашем файле `.env`: - -```ini -MIX_SENTRY_DSN_PUBLIC=http://example.com -``` - -После того, как переменная была определена в вашем файле `.env`, вы можете получить к ней доступ через объект `process.env`. Однако, вам нужно будет перезапустить задание, если значение переменной среды изменится во время ее выполнения: - -```js -process.env.MIX_SENTRY_DSN_PUBLIC -``` - - -## Уведомления - -Когда доступно, Mix будет автоматически отображать уведомления ОС при компиляции, давая вам мгновенную информацию о том, была ли компиляция успешной или нет. Однако, могут быть случаи, когда вы предпочтете отключить эти уведомления. Одним из таких примеров может быть запуск Mix на вашем рабочем сервере. Уведомления можно отключить с помощью метода `disableNotifications`: - -```js -mix.disableNotifications(); -``` +> **Примечание**\ +> Старое содержимое текущей страницы на русском языке временно доступно в руководстве [Компиляция веб-активов с помощью Mix](mix-old.md). diff --git a/docs/mocking.md b/docs/mocking.md index 59dfd5a..3c1cce4 100644 --- a/docs/mocking.md +++ b/docs/mocking.md @@ -123,7 +123,8 @@ Laravel предлагает полезные методы для имитаци } } -> {note} Вы не должны имитировать фасад `Request`. Вместо этого передайте требуемые данные в [методы тестирования HTTP](http-tests.md), такие как `get` и `post`, при запуске вашего теста. Аналогично, вместо имитации фасада `Config`, вызовите метод `Config::set` в ваших тестах. +> **Предупреждение**\ +> Вы не должны имитировать фасад `Request`. Вместо этого передайте требуемые данные в [методы тестирования HTTP](http-tests.md), такие как `get` и `post`, при запуске вашего теста. Аналогично, вместо имитации фасада `Config`, вызовите метод `Config::set` в ваших тестах. ### Шпионы фасадов @@ -197,6 +198,29 @@ Laravel предлагает полезные методы для имитаци return $job->order->id === $order->id; }); + +#### Фальсификация подмножества заданий + +Если вы хотите предотвратить отправку только определенных заданий, вы можете передать задания, которые должны быть сфальсифицированы, методу `fake`: + + /** + * Тест доставки заказа. + */ + public function test_orders_can_be_shipped() + { + Bus::fake([ + ShipOrder::class, + ]); + + // ... + } + +Вы можете сфальсифицировать все задания, кроме набора указанных, используя метод `except`: + + Bus::fake()->except([ + ShipOrder::class, + ]); + ### Цепочка заданий @@ -234,6 +258,18 @@ Laravel предлагает полезные методы для имитаци $batch->jobs->count() === 10; }); + +#### Тестирование взаимодействия задания и пакета заданий + +Кроме того, иногда требуется протестировать взаимодействие отдельного задания с пакетом заданий, частью которого тот является. Например, может потребоваться протестировать, не была ли отменена заданием дальнейшая обработка пакета заданий. Для этого вам нужно назначить заданию фальсифицированный пакет с помощью метода `withFakeBatch`. Метод `withFakeBatch` возвращает массив, содержащий экземпляр задания и фальсифицированный пакет: + + [$job, $batch] = (new ShipOrder)->withFakeBatch(); + + $job->handle(); + + $this->assertTrue($batch->cancelled()); + $this->assertEmpty($batch->added); + ## Фальсификация Event @@ -288,12 +324,13 @@ Laravel предлагает полезные методы для имитаци SendShipmentNotification::class ); -> {note} После вызова `Event::fake()` никакие слушатели событий выполняться не будут. Итак, если в ваших тестах используются фабрики моделей, которые полагаются на события, такие как создание UUID во время события `creating` модели, вы должны вызвать `Event::fake()` **после** использования ваших фабрик. +> **Предупреждение**\ +> После вызова `Event::fake()` никакие слушатели событий выполняться не будут. Итак, если в ваших тестах используются фабрики моделей, которые полагаются на события, такие как создание UUID во время события `creating` модели, вы должны вызвать `Event::fake()` **после** использования ваших фабрик. #### Фальсификация подмножества событий -Если вы хотите подделать слушателей событий только для определенного набора событий, вы можете передать их методу `fake` или `fakeFor`: +Если вы хотите сфальсифицировать слушателей событий только для определенного набора событий, вы можете передать их методу `fake` или `fakeFor`: /** * Тест обработки заказа. @@ -312,10 +349,16 @@ Laravel предлагает полезные методы для имитаци $order->update([...]); } +Вы можете сфальсифицировать все события, кроме набора указанных, используя метод `except`: + + Event::fake()->except([ + OrderCreated::class, + ]); + ### Ограниченная фальсификация событий -Если вы хотите подделать слушателей событий только для части вашего теста, вы можете использовать метод `fakeFor`: +Если вы хотите сфальсифицировать слушателей событий только для части вашего теста, вы можете использовать метод `fakeFor`: hasSubject('...'); }); +Экземпляр почтового отправления также содержит несколько полезных методов для проверки вложений в сообщении: + + use Illuminate\Mail\Mailables\Attachment; + + Mail::assertSent(OrderShipped::class, function ($mail) { + return $mail->hasAttachment( + Attachment::fromPath('/path/to/file') + ->as('name.pdf') + ->withMime('application/pdf') + ); + }); + + Mail::assertSent(OrderShipped::class, function ($mail) { + return $mail->hasAttachment( + Attachment::fromStorageDisk('s3', '/path/to/file') + ); + }); + + Mail::assertSent(OrderShipped::class, function ($mail) use ($pdfData) { + return $mail->hasAttachment( + Attachment::fromData(fn () => $pdfData, 'name.pdf') + ); + }); + Вы могли заметить, что есть два метода утверждения о том, что письмо не было отправлено: `assertNotSent` и `assertNotQueued`. При желании вы можете утверждать о том, что почта не была отправлена ​​**или** поставлена ​​в очередь. Для этого вы можете использовать методы `assertNothingOutgoing` и `assertNotOutgoing`: Mail::assertNothingOutgoing(); @@ -545,6 +612,20 @@ Laravel предлагает полезные методы для имитаци return $job->order->id === $order->id; }); +Если вам нужно сфальсифицировать только определенные задания, позволив другим заданиям выполниться нормально, то вы можете передать имена классов заданий, которые должны быть сфальсифицированы, в метод `fake`: + + public function test_orders_can_be_shipped() + { + Queue::fake([ + ShipOrder::class, + ]); + + // Выполняем доставку заказа... + + // Утверждаем, что задание было помещено дважды ... + Queue::assertPushed(ShipOrder::class, 2); + } + ### Цепочка заданий @@ -612,7 +693,8 @@ Laravel предлагает полезные методы для имитаци По умолчанию метод `fake` удалит все файлы во временном каталоге. Если вы хотите сохранить эти файлы, то вы можете вместо этого использовать метод `persistentFake`. Для получения дополнительной информации о тестировании загрузки файлов вы можете ознакомиться с [информацией по загрузке файлов из документации тестирования HTTP](http-tests.md#testing-file-uploads). -> {note} Для метода `image` требуется [расширение GD](https://www.php.net/manual/ru/book.image.php). +> **Предупреждение**\ +> Для метода `image` требуется [расширение GD](https://www.php.net/manual/ru/book.image.php). ## Взаимодействие со временем diff --git a/docs/notifications.md b/docs/notifications.md index c3a50aa..74b61a2 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -93,7 +93,8 @@ php artisan make:notification InvoicePaid $user->notify(new InvoicePaid($invoice)); -> {tip} Помните, что вы можете использовать трейт `Notifiable` в любой из ваших моделей. Вы не ограничены использованием его только в модели `User`. +> **Примечание**\ +> Помните, что вы можете использовать трейт `Notifiable` в любой из ваших моделей. Вы не ограничены использованием его только в модели `User`. ### Использование фасада `Notification` @@ -113,7 +114,8 @@ php artisan make:notification InvoicePaid Каждый класс уведомлений имеет метод `via`, который определяет, по каким каналам будет доставлено уведомление. Уведомления можно отправлять по каналам `mail`, `database`, `broadcast`, `vonage` и `slack`. -> {tip} Если вы хотите использовать другие каналы доставки, такие как Telegram или Pusher, то посетите веб-сайт сообщества [Laravel Notification Channels](http://laravel-notification-channels.com). +> **Примечание**\ +> Если вы хотите использовать другие каналы доставки, такие как Telegram или Pusher, то посетите веб-сайт сообщества [Laravel Notification Channels](http://laravel-notification-channels.com). Метод `via` получает экземпляр `$notifiable`, который будет экземпляром класса, которому отправляется уведомление. Вы можете использовать `$notifiable`, чтобы определить, по каким каналам должно доставляться уведомление: @@ -131,7 +133,8 @@ php artisan make:notification InvoicePaid ### Очереди уведомлений -> {note} Перед отправкой уведомлений в очередь вы должны настроить и запустить [обработчик очереди](queues.md). +> **Предупреждение**\ +> Перед отправкой уведомлений в очередь вы должны настроить и запустить [обработчик очереди](queues.md). Отправка уведомлений может занять время, особенно если каналу необходимо выполнить внешний вызов API для доставки уведомления. Чтобы ускорить время отклика вашего приложения, поместите ваше уведомление в очередь, добавив интерфейс `ShouldQueue` и трейт `Queueable` в ваш класс. Интерфейс и трейт уже импортированы для всех уведомлений, сгенерированных с помощью команды `make:notification`, поэтому вы можете сразу добавить их в свой класс уведомлений: @@ -203,6 +206,21 @@ php artisan make:notification InvoicePaid */ public $connection = 'redis'; +Или, если вы хотите указать конкретное соединение с очередью, которое должно использоваться для каждого канала уведомления, поддерживаемого уведомлением, вы можете определить метод `viaConnections` в своем уведомлении. Этот метод должен возвращать массив пар имя канала / имя подключения к очереди: + + /** + * Определить, какие соединения следует использовать для каждого канала уведомлений. + * + * @return array + */ + public function viaConnections() + { + return [ + 'mail' => 'redis', + 'database' => 'sync', + ]; + } + #### Изменение очереди канала уведомлений @@ -257,7 +275,8 @@ php artisan make:notification InvoicePaid } } -> {tip} Чтобы узнать больше о том, как обойти эти проблемы, просмотрите документацию, касающуюся [заданий в очереди и транзакций базы данных](queues.md#jobs-and-database-transactions). +> **Примечание**\ +> Чтобы узнать больше о том, как обойти эти проблемы, просмотрите документацию, касающуюся [заданий в очереди и транзакций базы данных](queues.md#jobs-and-database-transactions). #### Определение необходимости отправки уведомления в очереди @@ -283,9 +302,12 @@ php artisan make:notification InvoicePaid По желанию можно отправить уведомление кому-то, кто не сохранен как «пользователь» вашего приложения. Используя метод `route` фасада `Notification`, вы можете указать информацию о маршрутизации специального уведомления перед отправкой уведомления: + use Illuminate\Broadcasting\Channel; + Notification::route('mail', 'taylor@example.com') ->route('vonage', '5555555555') ->route('slack', 'https://hooks.slack.com/services/...') + ->route('broadcast', [new Channel('channel-name')]) ->notify(new InvoicePaid($invoice)); Если вы хотите указать имя получателя при отправке такого уведомления на маршрут `mail`, вы можете предоставить массив, содержащий адрес электронной почты в качестве ключа и имя в качестве значения первого элемента в массиве: @@ -317,22 +339,25 @@ php artisan make:notification InvoicePaid return (new MailMessage) ->greeting('Hello!') ->line('One of your invoices has been paid!') + ->lineIf($this->amount > 0, "Amount paid: {$this->amount}") ->action('View Invoice', $url) ->line('Thank you for using our application!'); } -> {tip} Обратите внимание, что мы используем `$this->invoice->id` в нашем методе `toMail`. Вы можете передать любые данные, которые необходимы вашему уведомлению для генерации сообщения, в конструктор уведомления. +> **Примечание**\ +> Обратите внимание, что мы используем `$this->invoice->id` в нашем методе `toMail`. Вы можете передать любые данные, которые необходимы вашему уведомлению для генерации сообщения, в конструктор уведомления. В этом примере мы регистрируем приветствие, строку текста, призыв к действию, а затем еще одну строку текста. Эти методы, предоставляемые объектом `MailMessage`, упрощают и ускоряют формирование небольших транзакционных электронных писем. Затем канал `mail` преобразует компоненты сообщения в красивый, отзывчивый HTML-шаблон сообщения электронной почты с аналогом в виде обычного текста. Вот пример электронного письма, созданного каналом `mail`: -> {tip} При отправке почтовых уведомлений не забудьте установить параметр `name` в вашем конфигурационном файле `config/app.php`. Это значение будет использоваться в верхнем и нижнем колонтитулах ваших почтовых уведомлений. +> **Примечание**\ +> При отправке почтовых уведомлений не забудьте установить параметр `name` в вашем конфигурационном файле `config/app.php`. Это значение будет использоваться в верхнем и нижнем колонтитулах ваших почтовых уведомлений. - -#### Другие параметры формирования почтовых уведомлений + +#### Сообщения об ошибках -Вместо определения «строк» текста в классе уведомления, вы можете использовать метод `view`, чтобы указать собственный шаблон, который следует использовать для отображения почтового уведомления: +Некоторые уведомления информируют пользователей об ошибках, например о неудачной оплате счета. Вы можете указать, что почтовое сообщение касается ошибки, вызвав метод `error` при построении вашего сообщения. При использовании метода `error` в почтовом сообщении кнопка призыва к действию будет красной вместо черной: /** * Получить содержимое почтового уведомления. @@ -342,12 +367,16 @@ php artisan make:notification InvoicePaid */ public function toMail($notifiable) { - return (new MailMessage)->view( - 'emails.name', ['invoice' => $this->invoice] - ); + return (new MailMessage) + ->error() + ->subject('Invoice Payment Failed') + ->line('...'); } -Вы можете определить текстовое содержимое для почтового сообщения, указав имя представления в качестве второго элемента массива, передаваемого методу `view`: + +#### Другие параметры формирования почтовых уведомлений + +Вместо определения «строк» текста в классе уведомления, вы можете использовать метод `view`, чтобы указать собственный шаблон, который следует использовать для отображения почтового уведомления: /** * Получить содержимое почтового уведомления. @@ -358,15 +387,11 @@ php artisan make:notification InvoicePaid public function toMail($notifiable) { return (new MailMessage)->view( - ['emails.name.html', 'emails.name.plain'], - ['invoice' => $this->invoice] + 'emails.name', ['invoice' => $this->invoice] ); } - -#### Сообщения об ошибках - -Некоторые уведомления информируют пользователей об ошибках, например о неудачной оплате счета. Вы можете указать, что почтовое сообщение касается ошибки, вызвав метод `error` при построении вашего сообщения. При использовании метода `error` в почтовом сообщении кнопка призыва к действию будет красной вместо черной: +Вы можете определить текстовое содержимое для почтового сообщения, указав имя представления в качестве второго элемента массива, передаваемого методу `view`: /** * Получить содержимое почтового уведомления. @@ -376,10 +401,10 @@ php artisan make:notification InvoicePaid */ public function toMail($notifiable) { - return (new MailMessage) - ->error() - ->subject('Notification Subject') - ->line('...'); + return (new MailMessage)->view( + ['emails.name.html', 'emails.name.plain'], + ['invoice' => $this->invoice] + ); } @@ -495,6 +520,9 @@ php artisan vendor:publish --tag=laravel-notifications ->attach('/path/to/file'); } +> **Примечание**\ +> Метод `attach` почтовых уведомлений также принимает [прикрепляемые объекты](mail.md#attachable-objects). + При прикреплении файлов к сообщению вы также можете указать отображаемое имя и / или MIME-тип, передав массив в качестве второго аргумента методу `attach`: /** @@ -530,6 +558,27 @@ php artisan vendor:publish --tag=laravel-notifications ->attachFromStorage('/path/to/file'); } +При необходимости к сообщению можно прикрепить несколько файлов с помощью метода `attachMany`: + + /** + * Получить содержимое почтового уведомления. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->greeting('Hello!') + ->attachMany([ + '/path/to/forge.svg', + '/path/to/vapor.svg' => [ + 'as' => 'Logo.svg', + 'mime' => 'image/svg+xml', + ], + ]); + } + #### Почтовые вложения необработанных данных @@ -572,7 +621,6 @@ php artisan vendor:publish --tag=laravel-notifications Если ваше приложение использует драйвер Mailgun, то вы можете обратиться к документации Mailgun для получения дополнительной информации о [тегах](https://documentation.mailgun.com/en/latest/user_manual.html#tagging-1) и [метаданных](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages). Кроме того, можно также обратиться к документации Postmark для получения дополнительной информации об их поддержке [тегов](https://postmarkapp.com/blog/tags-support-for-smtp) и [метаданных](https://postmarkapp.com/support/article/1125-custom-metadata-faq). Если ваше приложение использует Amazon SES для отправки электронных писем, то вам следует использовать метод `metadata` для прикрепления [тегов SES](https://docs.aws.amazon.com/ses/latest/APIReference/API_MessageTag.html) к сообщению. -Теги и метаданные могут быть добавлены в `MailMessage` – они используются вашей почтовой службой для фильтрации/обработки. ### Настройка сообщения Symfony @@ -692,18 +740,18 @@ php artisan make:notification InvoicePaid --markdown=mail.invoice.paid Почтовые уведомления Markdown используют комбинацию компонентов Blade и синтаксиса Markdown, которые позволяют легко создавать почтовые уведомления, используя предварительно созданные компоненты уведомлений Laravel: ```blade -@component('mail::message') + # Invoice Paid Your invoice has been paid! -@component('mail::button', ['url' => $url]) + View Invoice -@endcomponent + Thanks,
{{ config('app.name') }} -@endcomponent +
``` @@ -712,9 +760,9 @@ Thanks,
Компонент кнопки отображает ссылку на кнопку по центру. Компонент принимает два аргумента: `url` и необязательный `color`. Поддерживаемые цвета: `primary`, `green` и `red`. Вы можете добавить к уведомлению столько компонентов кнопки, сколько захотите: ```blade -@component('mail::button', ['url' => $url, 'color' => 'green']) + View Invoice -@endcomponent + ``` @@ -723,9 +771,9 @@ View Invoice Компонент панели отображает указанный блок текста на панели, цвет фона которой немного отличается от цвета остальной части сообщения. Это позволяет привлечь внимание к указанному блоку текста: ```blade -@component('mail::panel') + This is the panel content. -@endcomponent + ``` @@ -734,12 +782,12 @@ This is the panel content. Компонент таблицы позволяет преобразовать таблицу Markdown в таблицу HTML. Компонент принимает в качестве содержимого таблицу Markdown. Выравнивание столбцов таблицы поддерживается с использованием синтаксиса выравнивания таблицы Markdown по умолчанию: ```blade -@component('mail::table') + | Laravel | Table | Example | | ------------- |:-------------:| --------:| | Col 2 is | Centered | $10 | | Col 3 is | Right-Aligned | $20 | -@endcomponent + ``` @@ -835,7 +883,8 @@ php artisan migrate echo $notification->type; } -> {tip} Чтобы получить доступ к уведомлениям в JavaScript-приложении на клиентской стороне, вы должны определить контроллер уведомлений для своего приложения, который возвращает уведомления для уведомляемого объекта, такого как текущий пользователь. Затем вы можете сделать HTTP-запрос к URL-адресу этого контроллера из своего JavaScript-приложения на клиентской стороне. +> **Примечание**\ +> Чтобы получить доступ к уведомлениям в JavaScript-приложении на клиентской стороне, вы должны определить контроллер уведомлений для своего приложения, который возвращает уведомления для уведомляемого объекта, такого как текущий пользователь. Затем вы можете сделать HTTP-запрос к URL-адресу этого контроллера из своего JavaScript-приложения на клиентской стороне. ### Отметка прочитанных уведомлений @@ -1251,14 +1300,17 @@ Laravel позволяет отправлять уведомления, испо При отправке уведомления система уведомлений запускает [событие](events.md) `Illuminate\Notifications\Events\NotificationSending`. Событие содержит «уведомляемую» сущность и сам экземпляр уведомления. Как правило, регистрация слушателей этого события осуществляется в поставщике `App\Providers\EventServiceProvider`: + use App\Listeners\CheckNotificationStatus; + use Illuminate\Notifications\Events\NotificationSending; + /** * Карта слушателей событий приложения. * * @var array */ protected $listen = [ - 'Illuminate\Notifications\Events\NotificationSending' => [ - 'App\Listeners\CheckNotificationStatus', + NotificationSending::class => [ + CheckNotificationStatus::class, ], ]; @@ -1297,18 +1349,22 @@ Laravel позволяет отправлять уведомления, испо После отправки уведомления система уведомлений запускает [событие](events.md) `Illuminate\Notifications\Events\NotificationSent`. Событие содержит «уведомляемую» сущность и сам экземпляр уведомления. Как правило, регистрация слушателей этого события осуществляется в поставщике `App\Providers\EventServiceProvider`: + use App\Listeners\LogNotification; + use Illuminate\Notifications\Events\NotificationSent; + /** * Карта слушателей событий приложения. * * @var array */ protected $listen = [ - 'Illuminate\Notifications\Events\NotificationSent' => [ - 'App\Listeners\LogNotification', + NotificationSent::class => [ + LogNotification::class, ], ]; -> {tip} После регистрации слушателей в вашем `EventServiceProvider` используйте команду `event:generate` Artisan, чтобы быстро сгенерировать классы слушателей. +> **Примечание**\ +> После регистрации слушателей в вашем `EventServiceProvider` используйте команду `event:generate` Artisan, чтобы быстро сгенерировать классы слушателей. В слушателе события вы можете получить доступ к свойствам `notifiable`, `notification`, `channel` и `response` события для получения сведений о получателе уведомления или о самом уведомлении: diff --git a/docs/octane.md b/docs/octane.md index e660c61..fcad818 100644 --- a/docs/octane.md +++ b/docs/octane.md @@ -46,7 +46,8 @@ php artisan octane:install ## Server Prerequisites -> {note} Laravel Octane requires [PHP 8.0+](https://php.net/releases/). +> **Warning** +> Laravel Octane requires [PHP 8.0+](https://php.net/releases/). ### RoadRunner @@ -82,7 +83,7 @@ After installing the RoadRunner binary, you may exit your Sail shell session. Yo Next, update the `command` directive of your application's `docker/supervisord.conf` file so that Sail serves your application using Octane instead of the PHP development server: ```ini -command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port=8000 +command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port=80 ``` Finally, ensure the `rr` binary is executable and build your Sail images: @@ -105,7 +106,8 @@ pecl install swoole #### Swoole Via Laravel Sail -> {note} Before serving an Octane application via Sail, ensure you have the latest version of Laravel Sail and execute `./vendor/bin/sail build --no-cache` within your application's root directory. +> **Warning** +> Before serving an Octane application via Sail, ensure you have the latest version of Laravel Sail and execute `./vendor/bin/sail build --no-cache` within your application's root directory. Alternatively, you may develop your Swoole based Octane application using [Laravel Sail](/docs/{{version}}/sail), the official Docker based development environment for Laravel. Laravel Sail includes the Swoole extension by default. However, you will still need to adjust the `supervisor.conf` file used by Sail to keep your application running. To get started, execute the `sail:publish` Artisan command: @@ -136,7 +138,7 @@ Swoole supports a few additional configuration options that you may add to your 'log_file' => storage_path('logs/swoole_http.log'), 'package_max_length' => 10 * 1024 * 1024, ], -]; +], ``` @@ -162,7 +164,8 @@ By default, applications running via Octane generate links prefixed with `http:/ ### Serving Your Application Via Nginx -> {tip} If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Forge](https://forge.laravel.com). +> **Note** +> If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel Octane application, check out [Laravel Forge](https://forge.laravel.com). In production environments, you should serve your Octane application behind a traditional web server such as a Nginx or Apache. Doing so will allow the web server to serve your static assets such as images and stylesheets, as well as manage your SSL certificate termination. @@ -257,7 +260,7 @@ php artisan octane:start --workers=4 --task-workers=6 ### Specifying The Max Request Count -To help prevent stray memory leaks, Octane can gracefully restart a worker once it has handled a given number of requests. To instruct Octane to do this, you may use the `--max-requests` option: +To help prevent stray memory leaks, Octane gracefully restarts any worker once it has handled 500 requests. To adjust this number, you may use the `--max-requests` option: ```shell php artisan octane:start --max-requests=250 @@ -382,7 +385,8 @@ $service->method($request->input('name')); The global `request` helper will always return the request the application is currently handling and is therefore safe to use within your application. -> {note} It is acceptable to type-hint the `Illuminate\Http\Request` instance on your controller methods and route closures. +> **Warning** +> It is acceptable to type-hint the `Illuminate\Http\Request` instance on your controller methods and route closures. ### Configuration Repository Injection @@ -453,7 +457,8 @@ While building your application, you should take special care to avoid creating ## Concurrent Tasks -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may execute operations concurrently via light-weight background tasks. You may accomplish this using Octane's `concurrently` method. You may combine this method with PHP array destructuring to retrieve the results of each operation: @@ -474,10 +479,13 @@ Concurrent tasks processed by Octane utilize Swoole's "task workers", and execut php artisan octane:start --workers=4 --task-workers=6 ``` +When invoking the `concurrently` method, you should not provide more than 1024 tasks due to limitations imposed by Swoole's task system. + ## Ticks & Intervals -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may register "tick" operations that will be executed every specified number of seconds. You may register "tick" callbacks via the `tick` method. The first argument provided to the `tick` method should be a string that represents the name of the ticker. The second argument should be a callable that will be invoked at the specified interval. @@ -499,7 +507,8 @@ Octane::tick('simple-ticker', fn () => ray('Ticking...')) ## The Octane Cache -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may leverage the Octane cache driver, which provides read and write speeds of up to 2 million operations per second. Therefore, this cache driver is an excellent choice for applications that need extreme read / write speeds from their caching layer. @@ -509,7 +518,8 @@ This cache driver is powered by [Swoole tables](https://www.swoole.co.uk/docs/mo Cache::store('octane')->put('framework', 'Laravel', 30); ``` -> {tip} The maximum number of entries allowed in the Octane cache may be defined in your application's `octane` configuration file. +> **Note** +> The maximum number of entries allowed in the Octane cache may be defined in your application's `octane` configuration file. ### Cache Intervals @@ -527,7 +537,8 @@ Cache::store('octane')->interval('random', function () { ## Tables -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may define and interact with your own arbitrary [Swoole tables](https://www.swoole.co.uk/docs/modules/swoole-table). Swoole tables provide extreme performance throughput and the data in these tables can be accessed by all workers on the server. However, the data within them will be lost when the server is restarted. @@ -555,4 +566,5 @@ Octane::table('example')->set('uuid', [ return Octane::table('example')->get('uuid'); ``` -> {note} The column types supported by Swoole tables are: `string`, `int`, and `float`. +> **Warning** +> The column types supported by Swoole tables are: `string`, `int`, and `float`. diff --git a/docs/packages.md b/docs/packages.md index 82795a3..d7f64c5 100644 --- a/docs/packages.md +++ b/docs/packages.md @@ -11,6 +11,7 @@ - [Переводы](#translations) - [Шаблоны](#views) - [Компоненты шаблонов](#view-components) + - [Команда `about` Artisan](#about-artisan-command) - [Команды](#commands) - [Публичные веб-активы](#public-assets) - [Публикация групп файлов](#publishing-file-groups) @@ -107,7 +108,8 @@ $value = config('courier.option'); -> {note} Вы не должны определять замыкания в своих конфигурационных файлах. Они не могут быть корректно сериализованы, когда пользователи выполняют команду `config:cache` Artisan. +> **Предупреждение**\ +> Вы не должны определять замыкания в своих конфигурационных файлах. Они не могут быть корректно сериализованы, когда пользователи выполняют команду `config:cache` Artisan. #### Конфигурация пакета по умолчанию @@ -128,7 +130,8 @@ ); } -> {note} Этот метод объединяет только первый уровень массива конфигурации. Если ваши пользователи частично определяют многомерный массив конфигурации, то отсутствующие параметры не будут объединены. +> **Предупреждение**\ +> Этот метод объединяет только первый уровень массива конфигурации. Если ваши пользователи частично определяют многомерный массив конфигурации, то отсутствующие параметры не будут объединены. ### Маршруты @@ -308,6 +311,23 @@ Blade автоматически обнаружит класс, связанны ``` + +### Команда `about` Artisan + +В Laravel команда `about` Artisan предоставляет краткий обзор окружения и конфигурации приложения. Пакеты могут передавать дополнительную информацию при выводе информации этой команды через класс `AboutCommand`. Как правило, эта информация может быть добавлена в методе `boot` поставщика услуг вашего пакета: + + use Illuminate\Foundation\Console\AboutCommand; + + /** + * Загрузка любых служб пакета. + * + * @return void + */ + public function boot() + { + AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']); + } + ## Команды diff --git a/docs/pagination.md b/docs/pagination.md index 2b21d9d..c6c6a73 100644 --- a/docs/pagination.md +++ b/docs/pagination.md @@ -126,7 +126,8 @@ http://localhost/users?cursor=eyJpZCI6MTUsIl9wb2ludHNUb05leHRJdGVtcyI6dHJ1ZX0 После того, как вы получили экземпляр курсора, вы можете [отобразить результаты постраничной навигации](#displaying-pagination-results), как и обычно при использовании методов `paginate` и `simplePaginate`. Для получения дополнительной информации о методах экземпляра, предлагаемых пагинатором на основе курсора, обратитесь к [документации по методам экземпляра `CursorPaginator`](#cursor-paginator-instance-methods). -> {note} Ваш запрос должен содержать выражение `order by`, чтобы можно было использовать курсорную пагинацию. +> **Предупреждение**\ +> Ваш запрос должен содержать выражение `order by`, чтобы можно было использовать курсорную пагинацию. #### Курсорная пагинация против пагинации на основе смещения @@ -151,6 +152,7 @@ select * from users where id > 15 order by id asc limit 15; - Как и `simplePaginate`, курсорная пагинация может использоваться только для отображения ссылок «Далее» и «Назад» и не поддерживает создание ссылок с номерами страниц. - Требуется, чтобы порядок был основан как минимум на одном уникальном столбце или уникальной комбинации столбцов. Столбцы со значениями `null` не поддерживаются. - Выражения запроса в конструкциях `order by` поддерживаются только в том случае, если они имеют псевдонимы и добавлены в конструкцию `select`. +- Выражения запроса с параметрами не поддерживаются. ### Самостоятельное создание пагинатора @@ -161,7 +163,8 @@ select * from users where id > 15 order by id asc limit 15; Другими словами, `Paginator` соответствует методу `simplePaginate` построителя запросов, `CursorPaginator` – методу `cursorPaginate`, а `LengthAwarePaginator` – методу `paginate`. -> {note} При ручном создании экземпляра пагинатора вы должны самостоятельно «разрезать» массив результатов, который вы передаете в пагинатор. Если вы не знаете, как это сделать, ознакомьтесь с функцией PHP [`array_slice`](https://www.php.net/manual/ru/function.array-slice.php). +> **Предупреждение**\ +> При ручном создании экземпляра пагинатора вы должны самостоятельно «разрезать» массив результатов, который вы передаете в пагинатор. Если вы не знаете, как это сделать, ознакомьтесь с функцией PHP [`array_slice`](https://www.php.net/manual/ru/function.array-slice.php). ### Настройка URL-адресов постраничной навигации @@ -209,7 +212,7 @@ select * from users where id > 15 order by id asc limit 15; При вызове метода `paginate` вы получите экземпляр `Illuminate\Pagination\LengthAwarePaginator`. При вызове метода `simplePaginate` вы получите экземпляр `Illuminate\Pagination\Paginator`. И, наконец, вызов метода `cursorPaginate` возвращает экземпляр `Illuminate\Pagination\CursorPaginator`. -Эти объекты содержат несколько методов, описывающих результирующий набор. В дополнение к этим вспомогательным методам, экземпляры пагинатора являются итераторами и могут быть перебраны как массив. Итак, как только вы получили результаты, вы можете отобразить результаты и отрисовать ссылки на страницы, используя [Blade](blade.md): +Эти объекты содержат несколько методов, описывающих результирующий набор. В дополнение к этим методам, экземпляры пагинатора являются итераторами и могут быть перебраны как массив. Итак, как только вы получили результаты, вы можете отобразить результаты и отрисовать ссылки на страницы, используя [Blade](blade.md): ```blade
diff --git a/docs/passport.md b/docs/passport.md index aaa7c86..fe70607 100644 --- a/docs/passport.md +++ b/docs/passport.md @@ -10,6 +10,7 @@ - [Client Secret Hashing](#client-secret-hashing) - [Token Lifetimes](#token-lifetimes) - [Overriding Default Models](#overriding-default-models) + - [Overriding Routes](#overriding-routes) - [Issuing Access Tokens](#issuing-access-tokens) - [Managing Clients](#managing-clients) - [Requesting Tokens](#requesting-tokens) @@ -48,7 +49,8 @@ [Laravel Passport](https://github.com/laravel/passport) provides a full OAuth2 server implementation for your Laravel application in a matter of minutes. Passport is built on top of the [League OAuth2 server](https://github.com/thephpleague/oauth2-server) that is maintained by Andy Millington and Simon Hamp. -> {note} This documentation assumes you are already familiar with OAuth2. If you do not know anything about OAuth2, consider familiarizing yourself with the general [terminology](https://oauth2.thephpleague.com/terminology/) and features of OAuth2 before continuing. +> **Warning** +> This documentation assumes you are already familiar with OAuth2. If you do not know anything about OAuth2, consider familiarizing yourself with the general [terminology](https://oauth2.thephpleague.com/terminology/) and features of OAuth2 before continuing. ### Passport Or Sanctum? @@ -78,7 +80,8 @@ Next, you should execute the `passport:install` Artisan command. This command wi php artisan passport:install ``` -> {tip} If you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers, please install Passport using [the `uuids` option](#client-uuids). +> **Note** +> If you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers, please install Passport using [the `uuids` option](#client-uuids). After running the `passport:install` command, add the `Laravel\Passport\HasApiTokens` trait to your `App\Models\User` model. This trait will provide a few helper methods to your model which allow you to inspect the authenticated user's token and scopes. If your model is already using the `Laravel\Sanctum\HasApiTokens` trait, you may remove that trait: @@ -96,43 +99,7 @@ After running the `passport:install` command, add the `Laravel\Passport\HasApiTo use HasApiTokens, HasFactory, Notifiable; } -Next, you should call the `Passport::routes` method within the `boot` method of your `App\Providers\AuthServiceProvider`. This method will register the routes necessary to issue access tokens and revoke access tokens, clients, and personal access tokens: - - 'App\Policies\ModelPolicy', - ]; - - /** - * Register any authentication / authorization services. - * - * @return void - */ - public function boot() - { - $this->registerPolicies(); - - if (! $this->app->routesAreCached()) { - Passport::routes(); - } - } - } - -Finally, in your application's `config/auth.php` configuration file, you should set the `driver` option of the `api` authentication guard to `passport`. This will instruct your application to use Passport's `TokenGuard` when authenticating incoming API requests: +Finally, in your application's `config/auth.php` configuration file, you should define an `api` authentication guard and set the `driver` option to `passport`. This will instruct your application to use Passport's `TokenGuard` when authenticating incoming API requests: 'guards' => [ 'web' => [ @@ -175,8 +142,6 @@ If necessary, you may define the path where Passport's keys should be loaded fro { $this->registerPolicies(); - Passport::routes(); - Passport::loadKeysFrom(__DIR__.'/../secrets/oauth'); } @@ -243,14 +208,13 @@ By default, Passport issues long-lived access tokens that expire after one year. { $this->registerPolicies(); - Passport::routes(); - Passport::tokensExpireIn(now()->addDays(15)); Passport::refreshTokensExpireIn(now()->addDays(30)); Passport::personalAccessTokensExpireIn(now()->addMonths(6)); } -> {note} The `expires_at` columns on Passport's database tables are read-only and for display purposes only. When issuing tokens, Passport stores the expiration information within the signed and encrypted tokens. If you need to invalidate a token you should [revoke it](#revoking-tokens). +> **Warning** +> The `expires_at` columns on Passport's database tables are read-only and for display purposes only. When issuing tokens, Passport stores the expiration information within the signed and encrypted tokens. If you need to invalidate a token you should [revoke it](#revoking-tokens). ### Overriding Default Models @@ -269,6 +233,7 @@ After defining your model, you may instruct Passport to use your custom model vi use App\Models\Passport\AuthCode; use App\Models\Passport\Client; use App\Models\Passport\PersonalAccessClient; + use App\Models\Passport\RefreshToken; use App\Models\Passport\Token; /** @@ -280,14 +245,40 @@ After defining your model, you may instruct Passport to use your custom model vi { $this->registerPolicies(); - Passport::routes(); - Passport::useTokenModel(Token::class); - Passport::useClientModel(Client::class); + Passport::useRefreshTokenModel(RefreshToken::class); Passport::useAuthCodeModel(AuthCode::class); + Passport::useClientModel(Client::class); Passport::usePersonalAccessClientModel(PersonalAccessClient::class); } + +### Overriding Routes + +Sometimes you may wish to customize the routes defined by Passport. To achieve this, you first need to ignore the routes registered by Passport by adding `Passport::ignoreRoutes` to the `register` method of your application's `AppServiceProvider`: + + use Laravel\Passport\Passport; + + /** + * Register any application services. + * + * @return void + */ + public function register() + { + Passport::ignoreRoutes(); + } + +Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/11.x/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: + + Route::group([ + 'as' => 'passport.', + 'prefix' => config('passport.path', 'oauth'), + 'namespace' => 'Laravel\Passport\Http\Controllers', + ], function () { + // Passport routes... + }); + ## Issuing Access Tokens @@ -410,17 +401,25 @@ Once a client has been created, developers may use their client ID and secret to 'response_type' => 'code', 'scope' => '', 'state' => $state, + // 'prompt' => '', // "none", "consent", or "login" ]); return redirect('http://passport-app.test/oauth/authorize?'.$query); }); -> {tip} Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route. +The `prompt` parameter may be used to specify the authentication behavior of the Passport application. + +If the `prompt` value is `none`, Passport will always throw an authentication error if the user is not already authenticated with the Passport application. If the value is `consent`, Passport will always display the authorization approval screen, even if all scopes were previously granted to the consuming application. When the value is `login`, the Passport application will always prompt the user to re-login to the application, even if they already have an existing session. + +If no `prompt` value is provided, the user will be prompted for authorization only if they have not previously authorized access to the consuming application for the requested scopes. + +> **Note** +> Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. #### Approving The Request -When receiving authorization requests, Passport will automatically display a template to the user allowing them to approve or deny the authorization request. If they approve the request, they will be redirected back to the `redirect_uri` that was specified by the consuming application. The `redirect_uri` must match the `redirect` URL that was specified when the client was created. +When receiving authorization requests, Passport will automatically respond based on the value of `prompt` parameter (if present) and may display a template to the user allowing them to approve or deny the authorization request. If they approve the request, they will be redirected back to the `redirect_uri` that was specified by the consuming application. The `redirect_uri` must match the `redirect` URL that was specified when the client was created. If you would like to customize the authorization approval screen, you may publish Passport's views using the `vendor:publish` Artisan command. The published views will be placed in the `resources/views/vendor/passport` directory: @@ -428,7 +427,7 @@ If you would like to customize the authorization approval screen, you may publis php artisan vendor:publish --tag=passport-views ``` -Sometimes you may wish to skip the authorization prompt, such as when authorizing a first-party client. You may accomplish this by [extending the `Client` model](#overriding-default-models) and defining a `skipsAuthorization` method. If `skipsAuthorization` returns `true` the client will be approved and the user will be redirected back to the `redirect_uri` immediately: +Sometimes you may wish to skip the authorization prompt, such as when authorizing a first-party client. You may accomplish this by [extending the `Client` model](#overriding-default-models) and defining a `skipsAuthorization` method. If `skipsAuthorization` returns `true` the client will be approved and the user will be redirected back to the `redirect_uri` immediately, unless the consuming application has explicitly set the `prompt` parameter when redirecting for authorization: {tip} Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by the `Passport::routes` method. There is no need to manually define this route. +> **Note** +> Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by Passport. There is no need to manually define this route. #### JSON API @@ -628,6 +628,7 @@ Once a client has been created, you may use the client ID and the generated code 'state' => $state, 'code_challenge' => $codeChallenge, 'code_challenge_method' => 'S256', + // 'prompt' => '', // "none", "consent", or "login" ]); return redirect('http://passport-app.test/oauth/authorize?'.$query); @@ -667,7 +668,8 @@ If the state parameter matches, the consumer should issue a `POST` request to yo ## Password Grant Tokens -> {note} We no longer recommend using password grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). +> **Warning** +> We no longer recommend using password grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). The OAuth2 password grant allows your other first-party clients, such as a mobile application, to obtain an access token using an email address / username and password. This allows you to issue access tokens securely to your first-party clients without requiring your users to go through the entire OAuth2 authorization code redirect flow. @@ -683,7 +685,7 @@ php artisan passport:client --password ### Requesting Tokens -Once you have created a password grant client, you may request an access token by issuing a `POST` request to the `/oauth/token` route with the user's email address and password. Remember, this route is already registered by the `Passport::routes` method so there is no need to define it manually. If the request is successful, you will receive an `access_token` and `refresh_token` in the JSON response from the server: +Once you have created a password grant client, you may request an access token by issuing a `POST` request to the `/oauth/token` route with the user's email address and password. Remember, this route is already registered by Passport so there is no need to define it manually. If the request is successful, you will receive an `access_token` and `refresh_token` in the JSON response from the server: use Illuminate\Support\Facades\Http; @@ -698,7 +700,8 @@ Once you have created a password grant client, you may request an access token b return $response->json(); -> {tip} Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed. +> **Note** +> Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed. ### Requesting All Scopes @@ -783,7 +786,8 @@ When authenticating using the password grant, Passport will use the `password` a ## Implicit Grant Tokens -> {note} We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). +> **Warning** +> We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). The implicit grant is similar to the authorization code grant; however, the token is returned to the client without exchanging an authorization code. This grant is most commonly used for JavaScript or mobile applications where the client credentials can't be securely stored. To enable the grant, call the `enableImplicitGrant` method in the `boot` method of your application's `App\Providers\AuthServiceProvider` class: @@ -796,8 +800,6 @@ The implicit grant is similar to the authorization code grant; however, the toke { $this->registerPolicies(); - Passport::routes(); - Passport::enableImplicitGrant(); } @@ -814,12 +816,14 @@ Once the grant has been enabled, developers may use their client ID to request a 'response_type' => 'token', 'scope' => '', 'state' => $state, + // 'prompt' => '', // "none", "consent", or "login" ]); return redirect('http://passport-app.test/oauth/authorize?'.$query); }); -> {tip} Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route. +> **Note** +> Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. ## Client Credentials Grant Tokens @@ -873,7 +877,8 @@ To retrieve a token using this grant type, make a request to the `oauth/token` e Sometimes, your users may want to issue access tokens to themselves without going through the typical authorization code redirect flow. Allowing users to issue tokens to themselves via your application's UI can be useful for allowing users to experiment with your API or may serve as a simpler approach to issuing access tokens in general. -> {tip} If your application is primarily using Passport to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. +> **Note** +> If your application is primarily using Passport to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. ### Creating A Personal Access Client @@ -978,7 +983,8 @@ Passport includes an [authentication guard](/docs/{{version}}/authentication#add // })->middleware('auth:api'); -> {note} If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. +> **Warning** +> If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. #### Multiple Authentication Guards @@ -1001,7 +1007,8 @@ The following route will utilize the `api-customers` guard, which uses the `cust // })->middleware('auth:api-customers'); -> {tip} For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). +> **Note** +> For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). ### Passing The Access Token @@ -1036,8 +1043,6 @@ You may define your API's scopes using the `Passport::tokensCan` method in the ` { $this->registerPolicies(); - Passport::routes(); - Passport::tokensCan([ 'place-orders' => 'Place orders', 'check-status' => 'Check order status', @@ -1061,6 +1066,9 @@ If a client does not request any specific scopes, you may configure your Passpor 'place-orders', ]); +> **Note** +> Passport's default scopes do not apply to personal access tokens that are generated by the user. + ### Assigning Scopes To Tokens @@ -1159,7 +1167,8 @@ Typically, if you want to consume your API from your JavaScript application, you \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, ], -> {note} You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. +> **Warning** +> You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. This middleware will attach a `laravel_token` cookie to your outgoing responses. This cookie contains an encrypted JWT that Passport will use to authenticate API requests from your JavaScript application. The JWT has a lifetime equal to your `session.lifetime` configuration value. Now, since the browser will automatically send the cookie with all subsequent requests, you may make requests to your application's API without explicitly passing an access token: @@ -1182,8 +1191,6 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo { $this->registerPolicies(); - Passport::routes(); - Passport::cookie('custom_name'); } @@ -1192,7 +1199,8 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo When using this method of authentication, you will need to ensure a valid CSRF token header is included in your requests. The default Laravel JavaScript scaffolding includes an Axios instance, which will automatically use the encrypted `XSRF-TOKEN` cookie value to send an `X-XSRF-TOKEN` header on same-origin requests. -> {tip} If you choose to send the `X-CSRF-TOKEN` header instead of `X-XSRF-TOKEN`, you will need to use the unencrypted token provided by `csrf_token()`. +> **Note** +> If you choose to send the `X-CSRF-TOKEN` header instead of `X-XSRF-TOKEN`, you will need to use the unencrypted token provided by `csrf_token()`. ## Events diff --git a/docs/passwords.md b/docs/passwords.md index 57c7be0..5b3429b 100644 --- a/docs/passwords.md +++ b/docs/passwords.md @@ -15,7 +15,8 @@ Большинство веб-приложений предоставляют пользователям возможность сбросить забытые пароли. Вместо того, чтобы заставлять вас заново реализовывать этот функционал самостоятельно для каждого создаваемого вами приложения, Laravel предлагает удобные сервисы для отправки ссылок для сброса пароля и, собственно, безопасного сброса паролей. -> {tip} Хотите быстро начать? Установите один из [стартовых комплектов](starter-kits.md) в новое приложение Laravel. Стартовые комплекты позаботятся о построении всей вашей системы аутентификации, включая сброс забытых паролей. +> **Примечание**\ +> Хотите быстро начать? Установите один из [стартовых комплектов](starter-kits.md) в новое приложение Laravel. Стартовые комплекты позаботятся о построении всей вашей системы аутентификации, включая сброс забытых паролей. ### Подготовка модели @@ -87,7 +88,8 @@ php artisan migrate Вам может быть интересно: как Laravel знает о том, как получить запись пользователя из базы данных вашего приложения при вызове метода `sendResetLink` фасада `Password`? Брокер паролей Laravel использует «поставщиков пользователей» вашей системы аутентификации для получения записей из базы данных. Поставщик пользователей, используемый брокером паролей, настраивается в массиве `passwords` вашего файла конфигурации `config/auth.php`. Чтобы узнать больше о создании пользовательских поставщиков служб, обратитесь к [документации по аутентификации](authentication.md#adding-custom-user-providers). -> {tip} При выполнении сброса пароля самостоятельно, вы должны сами определять содержимое страницы и маршрутов. Если вам необходим каркас, включающий всю необходимую логику аутентификации и проверки, ознакомьтесь со [стартовыми комплектами приложений Laravel](starter-kits.md). +> **Примечание**\ +> При выполнении сброса пароля самостоятельно, вы должны сами определять содержимое страницы и маршрутов. Если вам необходим каркас, включающий всю необходимую логику аутентификации и проверки, ознакомьтесь со [стартовыми комплектами приложений Laravel](starter-kits.md). ### Сброс пароля diff --git a/docs/pint.md b/docs/pint.md new file mode 100644 index 0000000..2c39e38 --- /dev/null +++ b/docs/pint.md @@ -0,0 +1,152 @@ +# Laravel 9 · Pint + +- [Introduction](#introduction) +- [Installation](#installation) +- [Running Pint](#running-pint) +- [Configuring Pint](#configuring-pint) + - [Presets](#presets) + - [Rules](#rules) + - [Excluding Files / Folders](#excluding-files-or-folders) + + +## Introduction + +[Laravel Pint](https://github.com/laravel/pint) is an opinionated PHP code style fixer for minimalists. Pint is built on top of PHP-CS-Fixer and makes it simple to ensure that your code style stays clean and consistent. + +Pint is automatically installed with all new Laravel applications so you may start using it immediately. By default, Pint does not require any configuration and will fix code style issues in your code by following the opinionated coding style of Laravel. + + +## Installation + +Pint is included in recent releases of the Laravel framework, so installation is typically unnecessary. However, for older applications, you may install Laravel Pint via Composer: + +```shell +composer require laravel/pint --dev +``` + + +## Running Pint + +You can instruct Pint to fix code style issues by invoking the `pint` binary that is available in your project's `vendor/bin` directory: + +```shell +./vendor/bin/pint +``` + +You may also run Pint on specific files or directories: + +```shell +./vendor/bin/pint app/Models + +./vendor/bin/pint app/Models/User.php +``` + +Pint will display a thorough list of all of the files that it updates. You can view even more detail about Pint's changes by providing the `-v` option when invoking Pint: + +```shell +./vendor/bin/pint -v +``` + +If you would like Pint to simply inspect your code for style errors without actually changing the files, you may use the `--test` option: + +```shell +./vendor/bin/pint --test +``` + +If you would like Pint to only modify the files that have uncommitted changes according to Git, you may use the `--dirty` option: + +```shell +./vendor/bin/pint --dirty +``` + + +## Configuring Pint + +As previously mentioned, Pint does not require any configuration. However, if you wish to customize the presets, rules, or inspected folders, you may do so by creating a `pint.json` file in your project's root directory: + +```json +{ + "preset": "laravel" +} +``` + +In addition, if you wish to use a `pint.json` from a specific directory, you may provide the `--config` option when invoking Pint: + +```shell +pint --config vendor/my-company/coding-style/pint.json +``` + + +### Presets + +Presets defines a set of rules that can be used to fix code style issues in your code. By default, Pint uses the `laravel` preset, which fixes issues by following the opinionated coding style of Laravel. However, you may specify a different preset by providing the `--preset` option to Pint: + +```shell +pint --preset psr12 +``` + +If you wish, you may also set the preset in your project's `pint.json` file: + +```json +{ + "preset": "psr12" +} +``` + +Pint's currently supported presets are: `laravel`, `psr12`, and `symfony`. + + +### Rules + +Rules are style guidelines that Pint will use to fix code style issues in your code. As mentioned above, presets are predefined groups of rules that should be perfect for most PHP projects, so you typically will not need to worry about the individual rules they contain. + +However, if you wish, you may enable or disable specific rules in your `pint.json` file: + +```json +{ + "preset": "laravel", + "rules": { + "simplified_null_return": true, + "braces": false, + "new_with_braces": { + "anonymous_class": false, + "named_class": false + } + } +} +``` + +Pint is built on top of [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer). Therefore, you may use any of its rules to fix code style issues in your project: [PHP-CS-Fixer Configurator](https://mlocati.github.io/php-cs-fixer-configurator). + + +### Excluding Files / Folders + +By default, Pint will inspect all `.php` files in your project except those in the `vendor` directory. If you wish to exclude more folders, you may do so using the `exclude` configuration option: + +```json +{ + "exclude": [ + "my-specific/folder" + ] +} +``` + +If you wish to exclude all files that contain a given name pattern, you may do so using the `notName` configuration option: + +```json +{ + "notName": [ + "*-my-file.php" + ] +} +``` + +If you would like to exclude a file by providing an exact path to the file, you may do so using the `notPath` configuration option: + +```json +{ + "notPath": [ + "path/to/excluded-file.php" + ] +} +``` diff --git a/docs/providers.md b/docs/providers.md index 1648fff..a0aa428 100644 --- a/docs/providers.md +++ b/docs/providers.md @@ -18,7 +18,8 @@ В этой документации вы узнаете, как писать собственных поставщиков служб и регистрировать их в приложении Laravel. -> {tip} Если вы хотите узнать больше о том, как Laravel обрабатывает запросы и работает изнутри, ознакомьтесь с нашей документацией по [жизненному циклу запроса](lifecycle.md) Laravel. +> **Примечание**\ +> Если вы хотите узнать больше о том, как Laravel обрабатывает запросы и работает изнутри, ознакомьтесь с нашей документацией по [жизненному циклу запроса](lifecycle.md) Laravel. ## Написание поставщиков служб diff --git a/docs/queries.md b/docs/queries.md index 262d806..43d8fee 100644 --- a/docs/queries.md +++ b/docs/queries.md @@ -41,7 +41,8 @@ Построитель запросов Laravel использует связывание параметров PDO для защиты приложения от SQL-инъекций. Нет необходимости чистить строки, передаваемые как связываемые параметры. -> {note} PDO не поддерживает связывание имен столбцов. Поэтому, вы никогда не должны использовать какие-либо входящие от пользователя данные в качестве имен столбцов, используемые вашими запросами, включая столбцы в запросах `order by` и т.д. +> **Предупреждение**\ +> PDO не поддерживает связывание имен столбцов. Поэтому, вы никогда не должны использовать какие-либо входящие от пользователя данные в качестве имен столбцов, используемые вашими запросами, включая столбцы в запросах `order by` и т.д. ## Выполнение запросов к базе данных @@ -83,7 +84,8 @@ echo $user->name; } -> {tip} Коллекции Laravel содержат множество чрезвычайно мощных методов для перебора и сокращения данных. Для получения дополнительной информации о коллекциях Laravel ознакомьтесь с [их документацией](collections.md). +> **Примечание**\ +> Коллекции Laravel содержат множество чрезвычайно мощных методов для перебора и сокращения данных. Для получения дополнительной информации о коллекциях Laravel ознакомьтесь с [их документацией](collections.md). #### Получение одной строки / столбца из таблицы @@ -155,7 +157,8 @@ } }); -> {note} При обновлении или удалении записей внутри замыкания, любые изменения первичного или внешних ключей могут повлиять на запрос очередного фрагмента. Это может потенциально привести к тому, что записи могут не быть включены в последующие результаты выполнения замыкания. +> **Предупреждение**\ +> При обновлении или удалении записей внутри замыкания, любые изменения первичного или внешних ключей могут повлиять на запрос очередного фрагмента. Это может потенциально привести к тому, что записи могут не быть включены в последующие результаты выполнения замыкания. ### Отложенная потоковая передача результатов @@ -181,7 +184,8 @@ DB::table('users')->where('active', false) }); ``` -> {note} При обновлении или удалении записей во время их итерации любые изменения первичного ключа или внешних ключей могут повлиять на запрос фрагмента. Это может потенциально привести к тому, что записи не будут включены в результирующий набор. +> **Предупреждение**\ +> При обновлении или удалении записей во время их итерации любые изменения первичного ключа или внешних ключей могут повлиять на запрос фрагмента. Это может потенциально привести к тому, что записи не будут включены в результирующий набор. ### Извлечение Агрегатов @@ -248,7 +252,8 @@ DB::table('users')->where('active', false) ->groupBy('status') ->get(); -> {note} Сырые выражения будут вставлены в запрос в виде строк, поэтому следует проявлять особую осторожность, чтобы не создавать уязвимости для SQL-инъекций. +> **Предупреждение**\ +> Сырые выражения будут вставлены в запрос в виде строк, поэтому следует проявлять особую осторожность, чтобы не создавать уязвимости для SQL-инъекций. ### Необрабатываемые методы @@ -433,7 +438,8 @@ DB::table('users')->where('active', false) ['subscribed', '<>', '1'], ])->get(); -> {note} PDO не поддерживает связывание имен столбцов. Поэтому, вы никогда не должны использовать какие-либо входящие от пользователя данные в качестве имен столбцов, используемые вашими запросами, включая столбцы в запросах `order by` и т.д. +> **Предупреждение**\ +> PDO не поддерживает связывание имен столбцов. Поэтому, вы никогда не должны использовать какие-либо входящие от пользователя данные в качестве имен столбцов, используемые вашими запросами, включая столбцы в запросах `order by` и т.д. ### Выражения Or Where @@ -461,7 +467,8 @@ DB::table('users')->where('active', false) select * from users where votes > 100 or (name = 'Abigail' and votes > 50) ``` -> {note} Вы всегда должны группировать вызовы `orWhere`, чтобы избежать неожиданного поведения при применении [глобальных областей запроса](eloquent.md#query-scopes). +> **Предупреждение**\ +> Вы всегда должны группировать вызовы `orWhere`, чтобы избежать неожиданного поведения при применении [глобальных областей запроса](eloquent.md#query-scopes). ### Выражения Where Not @@ -478,13 +485,13 @@ select * from users where votes > 100 or (name = 'Abigail' and votes > 50) ### Выражения Where и JSON -Laravel также поддерживает запросы в базах данных, которые обеспечивают поддержку JSON-типов столбцов. В настоящее время это MySQL 5.7+, PostgreSQL, SQL Server 2016 и SQLite 3.9.0 (с [расширением JSON1](https://www.sqlite.org/json1.html)). Чтобы запросить столбец JSON, используйте оператор `->`: +Laravel также поддерживает запросы в базах данных, которые обеспечивают поддержку JSON-типов столбцов. В настоящее время это MySQL 5.7+, PostgreSQL, SQL Server 2016 и SQLite 3.39.0 (с [расширением JSON1](https://www.sqlite.org/json1.html)). Чтобы запросить столбец JSON, используйте оператор `->`: $users = DB::table('users') ->where('preferences->dining->meal', 'salad') ->get(); -Вы можете использовать `whereJsonContains` для запроса массивов JSON. Эта функция не поддерживается базой данных SQLite: +Вы можете использовать `whereJsonContains` для запроса массивов JSON. Эта функция не поддерживается базой данных SQLite ниже 3.38.0: $users = DB::table('users') ->whereJsonContains('options->languages', 'en') @@ -525,6 +532,20 @@ Laravel также поддерживает запросы в базах дан ->whereNotBetween('votes', [1, 100]) ->get(); +**whereBetweenColumns / whereNotBetweenColumns / orWhereBetweenColumns / orWhereNotBetweenColumns** + +Метод `whereBetweenColumns` проверяет, находится ли значение столбца между двумя значениями двух столбцов в одной и той же строке таблицы: + + $patients = DB::table('patients') + ->whereBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) + ->get(); + +Метод `whereNotBetweenColumns` проверяет, находится ли значение столбца за пределами двух значений двух столбцов в одной и той же строке таблицы: + + $patients = DB::table('patients') + ->whereNotBetweenColumns('weight', ['minimum_allowed_weight', 'maximum_allowed_weight']) + ->get(); + **whereIn / whereNotIn / orWhereIn / orWhereNotIn** Метод `whereIn` проверяет, что значение переданного столбца содержится в указанном массиве: @@ -539,7 +560,26 @@ Laravel также поддерживает запросы в базах дан ->whereNotIn('id', [1, 2, 3]) ->get(); -> {note} Если вы добавляете в свой запрос большой массив связываемых целочисленных параметров, то методы `whereIntegerInRaw` или `whereIntegerNotInRaw` могут использоваться для значительного сокращения потребляемой памяти. +Вы также можете передать объект запроса в качестве второго аргумента метода `whereIn`: + + $activeUsers = DB::table('users')->select('id')->where('is_active', 1); + + $users = DB::table('comments') + ->whereIn('user_id', $activeUsers) + ->get(); + +В приведенном выше примере будет получен следующий SQL: + +```sql +select * from comments where user_id in ( + select id + from users + where is_active = 1 +) +``` + +> **Предупреждение**\ +> Если вы добавляете в свой запрос большой массив связываемых целочисленных параметров, то методы `whereIntegerInRaw` или `whereIntegerNotInRaw` могут использоваться для значительного сокращения потребляемой памяти. **whereNull / whereNotNull / orWhereNull / orWhereNotNull** @@ -628,7 +668,8 @@ Laravel также поддерживает запросы в базах дан select * from users where name = 'John' and (votes > 100 or title = 'Admin') ``` -> {note} Вы всегда должны группировать вызовы `orWhere`, чтобы избежать неожиданного поведения при применении [глобальных областей запроса](eloquent.md#query-scopes). +> **Предупреждение**\ +> Вы всегда должны группировать вызовы `orWhere`, чтобы избежать неожиданного поведения при применении [глобальных областей запроса](eloquent.md#query-scopes). ### Расширенные выражения Where @@ -683,7 +724,8 @@ where exists ( ### Полнотекстовый поиск -> {note} Полнотекстовый поиск в настоящее время поддерживаются MySQL и PostgreSQL. +> **Предупреждение**\ +> Полнотекстовый поиск в настоящее время поддерживаются MySQL и PostgreSQL. Методы `whereFullText` и `orWhereFullText` полнотекстового поиска можно использовать для добавления условий `where` в запрос для столбцов, которые имеют [полнотекстовые индексы](migrations.md#available-index-types). Эти методы будут преобразованы Laravel в соответствующий SQL базы данных. Например, предложение `MATCH AGAINST` будет создано для приложений, использующих MySQL: @@ -861,21 +903,27 @@ where exists ( ['email' => 'john@example.com', 'votes' => 0] ); -> {note} При использовании PostgreSQL метод `insertGetId` ожидает, что автоинкрементный столбец будет называться `id`. Если вы хотите получить идентификатор из другой «последовательности», вы можете передать имя столбца в качестве второго параметра методу `insertGetId`. +> **Предупреждение**\ +> При использовании PostgreSQL метод `insertGetId` ожидает, что автоинкрементный столбец будет называться `id`. Если вы хотите получить идентификатор из другой «последовательности», вы можете передать имя столбца в качестве второго параметра методу `insertGetId`. ### Обновления-вставки Метод `upsert` вставляет записи, которые не существуют, и обновляет записи, которые уже существуют, новыми значениями, которые вы можете указать. Первый аргумент метода состоит из значений для вставки или обновления, а второй аргумент перечисляет столбцы, которые однозначно идентифицируют записи в связанной таблице. Третий и последний аргумент метода – это массив столбцов, который следует обновить, если соответствующая запись уже существует в базе данных: - DB::table('flights')->upsert([ - ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], - ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] - ], ['departure', 'destination'], ['price']); + DB::table('flights')->upsert( + [ + ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], + ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] + ], + ['departure', 'destination'], + ['price'] + ); В приведенном выше примере Laravel попытается вставить две записи. Если запись уже существует с такими же значениями столбцов `departure` и `destination`, то Laravel обновит столбец `price` этой записи. -> {note} Все базы данных, кроме SQL Server, требуют, чтобы столбцы во втором аргументе метода `upsert` имели «первичный» или «уникальный» индекс. Вдобавок, драйвер базы данных MySQL игнорирует второй аргумент метода `upsert` и всегда использует «первичный» и «уникальный» индексы таблицы для обнаружения существующих записей. +> **Предупреждение**\ +> Все базы данных, кроме SQL Server, требуют, чтобы столбцы во втором аргументе метода `upsert` имели «первичный» или «уникальный» индекс. Вдобавок, драйвер базы данных MySQL игнорирует второй аргумент метода `upsert` и всегда использует «первичный» и «уникальный» индексы таблицы для обнаружения существующих записей. ## Обновление @@ -921,10 +969,17 @@ where exists ( DB::table('users')->decrement('votes', 5); -Вы также можете указать дополнительные столбцы для обновления во время выполнения запроса: +Вы также можете указать дополнительные столбцы для обновления во время операции увеличения или уменьшения: DB::table('users')->increment('votes', 1, ['name' => 'John']); +Кроме того, вы можете одновременно увеличивать или уменьшать несколько столбцов, используя методы `incrementEach` и `decrementEach`: + + DB::table('users')->incrementEach([ + 'votes' => 5, + 'balance' => 100, + ]); + ## Удаление diff --git a/docs/queues.md b/docs/queues.md index 2a8c2e3..353d002 100644 --- a/docs/queues.md +++ b/docs/queues.md @@ -55,7 +55,8 @@ Параметры конфигурации очереди Laravel хранятся в файле конфигурации вашего приложения `config/queue.php`. В этом файле вы найдете конфигурации подключения для каждого из драйверов очереди фреймворка: база данных, [Amazon SQS](https://aws.amazon.com/sqs/), [Redis](https://redis.io) и [Beanstalkd](https://beanstalkd.github.io/), а также синхронный драйвер для немедленного выполнения задания (используется во время локальной разработки). Также имеется драйвер очереди `null`, который выбрасывает задания из очереди. -> {tip} Laravel также предлагает Horizon, красивую панель управления и систему конфигурации для ваших очередей с поддержкой Redis. Дополнительную информацию можно найти в полной [документации Horizon](horizon.md). +> **Примечание**\ +> Laravel также предлагает Horizon, красивую панель управления и систему конфигурации для ваших очередей с поддержкой Redis. Дополнительную информацию можно найти в полной [документации Horizon](horizon.md). ### Соединения и очереди @@ -126,7 +127,8 @@ php artisan migrate 'block_for' => 5, ], -> {note} Установка для `block_for` значения `0` заставит обработчиков очереди блокироваться на неопределенный срок, пока задание не станет доступным. Это также предотвратит обработку таких сигналов, как `SIGTERM`, до тех пор, пока не будет обработано следующее задание. +> **Предупреждение**\ +> Установка для `block_for` значения `0` заставит обработчиков очереди блокироваться на неопределенный срок, пока задание не станет доступным. Это также предотвратит обработку таких сигналов, как `SIGTERM`, до тех пор, пока не будет обработано следующее задание. #### Дополнительные зависимости драйверов @@ -155,7 +157,8 @@ php artisan make:job ProcessPodcast Сгенерированный класс будет реализовывать интерфейс `Illuminate\Contracts\Queue\ShouldQueue`, указывая Laravel, что задание должно быть поставлено в очередь для асинхронного выполнения. -> {tip} Заготовки заданий можно настроить с помощью [публикации заготовок](artisan.md#stub-customization). +> **Примечание**\ +> Заготовки заданий можно настроить с помощью [публикации заготовок](artisan.md#stub-customization). ### Структура класса задания @@ -226,7 +229,8 @@ php artisan make:job ProcessPodcast return $job->handle($app->make(AudioProcessor::class)); }); -> {note} Двоичные данные, например, необработанное содержимое изображения, должны быть переданы через функцию `base64_encode` перед передачей заданию. В противном случае задание может неправильно сериализоваться в JSON при отправке в очередь. +> **Предупреждение**\ +> Двоичные данные, например, необработанное содержимое изображения, должны быть переданы через функцию `base64_encode` перед передачей заданию. В противном случае задание может неправильно сериализоваться в JSON при отправке в очередь. #### Игнорирование отношений при создании задания @@ -249,7 +253,8 @@ php artisan make:job ProcessPodcast ### Уникальные задания -> {note} Для уникальных заданий требуется драйвер кеша, поддерживающий [блокировки](cache.md#atomic-locks). В настоящее время драйверы кеширования `memcached`, `redis`, `dynamodb`, `database`, `file`, и `array` поддерживают атомарные блокировки. Кроме того, уникальность заданий не учитывается при пакетной обработке. +> **Предупреждение**\ +> Для уникальных заданий требуется драйвер кеша, поддерживающий [блокировки](cache.md#atomic-locks). В настоящее время атомарные блокировки поддерживают драйверы кеширования `memcached`, `redis`, `dynamodb`, `database`, `file` и `array`. Кроме того, уникальность заданий не учитывается при пакетной обработке. Иногда требуется убедиться, что только один экземпляр определенного задания находится в очереди в любой момент времени. Вы можете сделать это, реализовав интерфейс `ShouldBeUnique` в своем классе задания. Этот интерфейс не требует от вас определения каких-либо дополнительных методов в вашем классе: @@ -302,7 +307,8 @@ php artisan make:job ProcessPodcast В приведенном выше примере задание `UpdateSearchIndex` уникально по идентификатору продукта. Таким образом, любые новые отправленные задания с тем же идентификатором продукта будут игнорироваться, пока существующее задание не завершит обработку. Кроме того, если существующее задание не будет обработано в течение одного часа, уникальная блокировка будет снята, и в очередь может быть отправлено другое задание с таким же уникальным ключом. -> {note} Если ваше приложение отправляет задания с нескольких веб-серверов или контейнеров, то вы должны убедиться, что все ваши серверы взаимодействуют с одним и тем же центральным кэш-сервером, чтобы Laravel мог точно определить, является ли задание уникальным. +> **Предупреждение**\ +> Если ваше приложение отправляет задания с нескольких веб-серверов или контейнеров, то вы должны убедиться, что все ваши серверы взаимодействуют с одним и тем же центральным кэш-сервером, чтобы Laravel мог точно определить, является ли задание уникальным. #### Сохранение уникальности задания только до начала обработки @@ -342,7 +348,8 @@ php artisan make:job ProcessPodcast } } -> {tip} Если вам нужно ограничить только параллельную обработку задания, используйте вместо этого посредник [`WithoutOverlapping`](#preventing-job-overlaps). +> **Примечание**\ +> Если вам нужно ограничить только параллельную обработку задания, используйте вместо этого посредник [`WithoutOverlapping`](#preventing-job-overlaps). ## Посредник задания @@ -420,7 +427,8 @@ php artisan make:job ProcessPodcast return [new RateLimited]; } -> {tip} Посредник задания также можно назначить слушателям событий в очереди, почтовым отправлениям и уведомлениям. +> **Примечание**\ +> Посредник задания также можно назначить слушателям событий в очереди, почтовым отправлениям и уведомлениям. ### Ограничение частоты @@ -478,7 +486,8 @@ php artisan make:job ProcessPodcast return [(new RateLimited('backups'))->dontRelease()]; } -> {tip} Если вы используете Redis, то вы можете использовать посредника `Illuminate\Queue\Middleware\RateLimitedWithRedis`, который лучше настроен для Redis и более эффективен, чем базовый посредник с ограничением частоты. +> **Примечание**\ +> Если вы используете Redis, то вы можете использовать посредника `Illuminate\Queue\Middleware\RateLimitedWithRedis`, который лучше настроен для Redis и более эффективен, чем базовый посредник с ограничением частоты. ### Предотвращение дублирования задания @@ -499,7 +508,7 @@ Laravel содержит посредника `Illuminate\Queue\Middleware\Witho return [new WithoutOverlapping($this->user->id)]; } -Любые перекрывающиеся задания будут возвращены в очередь. Можно также указать время в секундах, которое должно пройти до повторной попытки возвращенного задания: +Любые перекрывающиеся задания одного и того же типа будут возвращены в очередь. Можно также указать время в секундах, которое должно пройти до повторной попытки возвращенного задания: /** * Получить посредника, через которого должно пройти задание. @@ -535,7 +544,43 @@ Laravel содержит посредника `Illuminate\Queue\Middleware\Witho return [(new WithoutOverlapping($this->order->id))->expireAfter(180)]; } -> {note} Для посредника `WithoutOverlapping` требуется драйвер кеша, который поддерживает [блокировки](cache.md#atomic-locks). В настоящее время драйверы кеша `memcached`, `redis`, `dynamodb`, `database`, `file` и `array` поддерживают атомарные блокировки. +> **Предупреждение**\ +> Для посредника `WithoutOverlapping` требуется драйвер кеша, который поддерживает [блокировки](cache.md#atomic-locks). В настоящее время атомарные блокировки поддерживают драйверы кеширования `memcached`, `redis`, `dynamodb`, `database`, `file` и `array`. + + +#### Совместное использование ключей блокировки для разных классов заданий + +По умолчанию посредник `WithoutOverlapping` предотвращает перекрытие заданий только одного класса. Таким образом, хотя два разных класса заданий могут использовать один и тот же ключ блокировки, они не будут защищены от перекрытия. Однако вы можете указать Laravel применять ключ к классам заданий, используя метод `shared`: + +```php +use Illuminate\Queue\Middleware\WithoutOverlapping; + +class ProviderIsDown +{ + // ... + + + public function middleware() + { + return [ + (new WithoutOverlapping("status:{$this->provider}"))->shared(), + ]; + } +} + +class ProviderIsUp +{ + // ... + + + public function middleware() + { + return [ + (new WithoutOverlapping("status:{$this->provider}"))->shared(), + ]; + } +} +``` ### Ограничение частоты генерации исключений @@ -596,7 +641,8 @@ Laravel содержит посредника `Illuminate\Queue\Middleware\Throt return [(new ThrottlesExceptions(10, 10))->by('key')]; } -> {tip} Если вы используете Redis в качестве драйвера кеша вашего приложения, то вы можете использовать класс `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis`. Этот класс более эффективен при управлении ограничениями исключений с помощью Redis. +> **Примечание**\ +> Если вы используете Redis в качестве драйвера кеша вашего приложения, то вы можете использовать класс `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis`. Этот класс более эффективен при управлении ограничениями исключений с помощью Redis. ## Отправка заданий @@ -636,6 +682,8 @@ Laravel содержит посредника `Illuminate\Queue\Middleware\Throt ProcessPodcast::dispatchUnless($accountSuspended, $podcast); +В новых приложениях Laravel драйвер `sync` является драйвером очереди по умолчанию. Этот драйвер выполняет задания синхронно совместно с текущим запросом, что часто удобно при локальной разработке. Если вы действительно хотите начать ставить задания в очередь для фоновой обработки, то вы можете указать другой драйвер очереди в конфигурационном файле `config/queue.php` вашего приложения. + ### Отложенная отправка @@ -669,12 +717,13 @@ Laravel содержит посредника `Illuminate\Queue\Middleware\Throt } } -> {note} У сервиса очередей Amazon SQS максимальное время задержки составляет 15 минут. +> **Предупреждение**\ +> У сервиса очередей Amazon SQS максимальное время задержки составляет 15 минут. #### Отправка задания после отправки ответа в браузер -В качестве альтернативы, метод `dispatchAfterResponse` задерживает отправку задания до тех пор, пока HTTP-ответ не будет отправлен в браузер пользователя. Это по-прежнему позволит пользователю начать использовать приложение, даже если задание в очереди все еще выполняется. Обычно это следует использовать только для заданий, которые занимают около секунды, например, для отправки электронного письма. Поскольку они обрабатываются в рамках текущего HTTP-запроса, отправляемые таким образом задания не требуют запуска обработчика очереди для их обработки: +В качестве альтернативы, метод `dispatchAfterResponse` задерживает отправку задания до тех пор, пока HTTP-ответ не будет отправлен в браузер пользователя, если ваш веб-сервер использует FastCGI. Это по-прежнему позволит пользователю начать использовать приложение, даже если задание в очереди все еще выполняется. Обычно это следует использовать только для заданий, которые занимают около секунды, например, для отправки электронного письма. Поскольку они обрабатываются в рамках текущего HTTP-запроса, отправляемые таким образом задания не требуют запуска обработчика очереди для их обработки: use App\Jobs\SendNotification; @@ -738,7 +787,8 @@ Laravel содержит посредника `Illuminate\Queue\Middleware\Throt Если транзакция откатывается из-за исключения, возникшего во время транзакции, то задания, которые были отправлены во время этой транзакции, будут отброшены. -> {tip} Установка параметру конфигурации `after_commit` значения `true` также вызовет отправку всех поставленных в очередь слушателей событий, почтовых отправлений, уведомлений и широковещательных событий после того, как все открытые транзакции базы данных были зафиксированы. +> **Примечание**\ +> Установка параметру конфигурации `after_commit` значения `true` также вызовет отправку всех поставленных в очередь слушателей событий, почтовых отправлений, уведомлений и широковещательных событий после того, как все открытые транзакции базы данных были зафиксированы. #### Непосредственное указание поведения отправки при фиксации транзакций БД @@ -779,7 +829,8 @@ Laravel содержит посредника `Illuminate\Queue\Middleware\Throt }, ])->dispatch(); -> {note} Удаление заданий с помощью метода `$this->delete()` внутри задания не остановить обработку связанных заданий. Цепочка прекратит выполнение только в случае сбоя задания в цепочке. +> **Предупреждение**\ +> Удаление заданий с помощью метода `$this->delete()` внутри задания не остановить обработку связанных заданий. Цепочка прекратит выполнение только в случае сбоя задания в цепочке. #### Соединения и очередь цепочки заданий @@ -808,6 +859,8 @@ Laravel содержит посредника `Illuminate\Queue\Middleware\Throt // Задание в цепочке не выполнено ... })->dispatch(); +> **Примечание**\ Поскольку замыкания в цепочке заданий сериализуются и выполняются позже очередью Laravel, то вы не должны использовать переменную `$this` в замыканиях цепочек заданий. + ### Настройка соединения и очереди @@ -943,13 +996,13 @@ Laravel содержит посредника `Illuminate\Queue\Middleware\Throt Если в одном из ваших заданий в очереди обнаруживается ошибка, то вы, вероятно, не хотите, чтобы оно продолжало повторять попытки бесконечно. Laravel предлагает различные способы указать, сколько раз и как долго задание может повторно выполняться. -Один из подходов к указанию максимального количества попыток выполнения задания – это использование переключателя `--tries` в командной строке Artisan. Это будет применяться ко всем заданиям обработчика, если только обрабатываемое задание не указывает более конкретное количество попыток его выполнения: +Один из подходов к указанию максимального количества попыток выполнения задания – это использование параметра `--tries` в командной строке Artisan. Это будет применяться ко всем заданиям обработчика, если только в обрабатываемом задании не указано количество попыток его выполнения: ```shell php artisan queue:work --tries=3 ``` -Если задание превышает максимальное количество попыток, то оно будет считаться «неудачным». Для получения дополнительной информации об обработке невыполненных заданий обратитесь к [документации по разбору неудачных заданий](#dealing-with-failed-jobs). +Если задание превышает максимальное количество попыток, то оно будет считаться «неудачным». Для получения дополнительной информации об обработке невыполненных заданий обратитесь к [документации по разбору неудачных заданий](#dealing-with-failed-jobs). Если для команды `queue:work` указан парамет `--tries=0`, то задание будет повторяться неограниченное количество раз. Вы можете применить более детальный подход, указав максимальное количество попыток выполнения задания для самого класса задания. Если для задания указано максимальное количество попыток, оно будет иметь приоритет над значением `--tries`, указанным в командной строке: @@ -982,7 +1035,8 @@ php artisan queue:work --tries=3 return now()->addMinutes(10); } -> {tip} Вы также можете определить свойство `$tries` или метод `retryUntil` в ваших [слушателях событий](events.md#queued-event-listeners). +> **Примечание**\ +> Вы также можете определить свойство `$tries` или метод `retryUntil` в ваших [слушателях событий](events.md#queued-event-listeners). #### Максимальное количество исключений @@ -1032,9 +1086,10 @@ php artisan queue:work --tries=3 #### Тайм-аут -> {note} Для указания тайм-аутов задания должно быть установлено расширение `pcntl` PHP. +> **Предупреждение**\ +> Для указания тайм-аутов задания должно быть установлено расширение `pcntl` PHP. -Часто вы приблизительно знаете, сколько времени займет выполнение заданий в очереди. По этой причине Laravel позволяет вам указать значение «тайм-аута». Если задание обрабатывается дольше, чем количество секунд, указанное значением тайм-аута, обработчик завершится с ошибкой. Обычно обработчик перезапускается автоматически [диспетчером, настроенным на вашем сервере](#supervisor-configuration). +Часто вы приблизительно знаете, сколько времени займет выполнение заданий в очереди. По этой причине Laravel позволяет вам указать значение «тайм-аута». По умолчанию значение времени ожидания равно 60 секундам. Если задание обрабатывается дольше, чем количество секунд, указанное значением тайм-аута, обработчик завершится с ошибкой. Обычно обработчик перезапускается автоматически [диспетчером, настроенным на вашем сервере](#supervisor-configuration). Максимальное количество секунд, в течение которых могут выполняться задания, можно указать с помощью переключателя `--timeout` в командной строке Artisan: @@ -1119,11 +1174,14 @@ public $failOnTimeout = true; $this->fail(); } -Если вы хотите пометить свою работу как неудавшуюся из-за обнаруженного исключения, то вы можете передать исключение методу `fail`: +Если вы хотите пометить свою работу как неудавшуюся из-за обнаруженного исключения, то вы можете передать исключение методу `fail`. Или, для удобства, вы можете передать строковое сообщение об ошибке, которое будет автоматически преобразовано в исключение: $this->fail($exception); -> {tip} Для получения дополнительной информации об обработке невыполненных заданий обратитесь к [документации по разбору неудачных заданий](#dealing-with-failed-jobs). + $this->fail('Something went wrong.'); + +> **Примечание**\ +> Для получения дополнительной информации об обработке невыполненных заданий обратитесь к [документации по разбору неудачных заданий](#dealing-with-failed-jobs). ## Пакетная обработка заданий @@ -1201,7 +1259,8 @@ php artisan migrate Идентификатор пакета, к которому можно получить доступ через свойство `$batch->id`, можно использовать для [запроса к командной шине Laravel](#inspecting-batches) для получения информации о пакете после того, как он был отправлен. -> {note} Поскольку замыкания сериализуются и выполняются позже очередью Laravel, то вам не следует использовать переменную `$this` в замыканиях. +> **Предупреждение**\ +> Поскольку замыкания в пакете заданий сериализуются и выполняются позже очередью Laravel, то вы не должны использовать переменную `$this` в замыканиях пакета заданий. #### Именованные пакеты заданий @@ -1282,7 +1341,8 @@ php artisan migrate })); } -> {note} Вы можете добавлять задания в пакет только из задания, которое принадлежит к тому же пакету. +> **Предупреждение**\ +> Вы можете добавлять задания в пакет только из задания, которое принадлежит к тому же пакету. ### Инспектирование пакета @@ -1410,6 +1470,10 @@ php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5 $schedule->command('queue:prune-batches --hours=48 --unfinished=72')->daily(); +Так же ваша таблица `jobs_batches` может накапливать записи об отмененных пакетах. Вы можете указать команде `queue:prune-batches` удалять записи об отмененных пакетах, используя опцию `cancelled`: + + $schedule->command('queue:prune-batches --hours=48 --cancelled=72')->daily(); + ## Анонимные очереди @@ -1431,6 +1495,8 @@ php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5 // Это задание завершилось неудачно ... }); +> **Примечание**\ Поскольку замыкания в `catch` сериализуются и выполняются позже очередью Laravel, то вы не должны использовать переменную `$this` в замыканиях `catch`. + ## Запуск обработчика очереди @@ -1443,7 +1509,14 @@ Laravel включает команду Artisan, которая запускае php artisan queue:work ``` -> {tip} Чтобы процесс `queue:work` постоянно работал в фоновом режиме, вы должны использовать диспетчер процессов, такой как [Supervisor](#supervisor-configuration), чтобы гарантировать, что обработчик очереди не перестанет работать. +> **Примечание**\ +> Чтобы процесс `queue:work` постоянно работал в фоновом режиме, вы должны использовать диспетчер процессов, такой как [Supervisor](#supervisor-configuration), чтобы гарантировать, что обработчик очереди не перестанет работать. + +Вы можете использовать флаг `-v` при вызове команды `queue:work`, если вы хотите, чтобы идентификаторы обработанных заданий были включены в вывод команды: + +```shell +php artisan queue:work -v +``` Помните, что обработчики очереди – это долгоживущие процессы, которые хранят состояние загруженного приложения в памяти. В результате они не заметят изменений в вашей кодовой базе после их запуска. Итак, во время процесса развертывания обязательно [перезапустите своих обработчиков очереди](#queue-workers-and-deployment). Кроме того, помните, что любое статическое состояние, созданное или измененное вашим приложением, не будет автоматически пробрасываться между заданиями. @@ -1545,7 +1618,8 @@ php artisan queue:restart Эта команда укажет всем обработчикам очереди корректно выйти после завершения обработки своего текущего задания, чтобы существующие задания не были потеряны. Поскольку обработчики очереди выйдут при выполнении команды `queue:restart`, вы должны запустить диспетчер процессов, такой как [Supervisor](#supervisor-configuration), для автоматического перезапуска обработчиков очереди. -> {tip} Очередь использует [кеш](cache.md) для хранения сигналов перезапуска, поэтому перед использованием этой функции необходимо убедиться, что драйвер кеша правильно настроен для приложения. +> **Примечание**\ +> Очередь использует [кеш](cache.md) для хранения сигналов перезапуска, поэтому перед использованием этой функции необходимо убедиться, что драйвер кеша правильно настроен для приложения. ### Истечение срока и тайм-ауты задания @@ -1555,12 +1629,13 @@ php artisan queue:restart В вашем файле конфигурации `config/queue.php` каждое соединение с очередью определяет параметр `retry_after`. Этот параметр указывает, сколько секунд соединение очереди должно ждать перед повторной попыткой выполнения задания, которое обрабатывается. Например, если значение `retry_after` установлено на `90`, задание будет возвращено в очередь, если оно обрабатывалось в течение 90 секунд, но не было высвобождено или удалено. Как правило, вы должны установить значение `retry_after` на максимальное количество секунд, которое может потребоваться вашим заданиям для завершения обработки. -> {note} Единственное соединение очереди, которое не содержит значения `retry_after` – это Amazon SQS. SQS будет повторять выполнение задания в соответствии с [таймаутом видимости по умолчанию](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html), управляемый консолью AWS. +> **Предупреждение**\ +> Единственное соединение очереди, которое не содержит значения `retry_after` – это Amazon SQS. SQS будет повторять выполнение задания в соответствии с [таймаутом видимости по умолчанию](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html), управляемый консолью AWS. #### Тайм-ауты обработчиков -Команда `queue:work` Artisan также содержит параметр `--timeout`. Если задание обрабатывается дольше, чем количество секунд, указанное значением тайм-аута, Обработчик, выполняющий задание, завершится с ошибкой. Обычно обработчик перезапускается автоматически [диспетчером, настроенным на вашем сервере](#supervisor-configuration): +Команда `queue:work` Artisan также содержит параметр `--timeout`. По умолчанию значение времени ожидания равно 60 секундам. Если задание обрабатывается дольше, чем количество секунд, указанное значением тайм-аута, Обработчик, выполняющий задание, завершится с ошибкой. Обычно обработчик перезапускается автоматически [диспетчером, настроенным на вашем сервере](#supervisor-configuration): ```shell php artisan queue:work --timeout=60 @@ -1568,7 +1643,8 @@ php artisan queue:work --timeout=60 Параметр конфигурации `retry_after` и параметр `--timeout` Artisan отличаются, но работают вместе, чтобы гарантировать, что задания не будут потеряны и что задания будут успешно обработаны только один раз. -> {note} Значение `--timeout` всегда должно быть как минимум на несколько секунд короче, чем ваше значение конфигурации `retry_after`. Это гарантирует, что обрабатывающий замороженное задание обработчик, всегда завершает работу перед повторной попыткой выполнения задания. Если параметр `--timeout` выше значения конфигурации `retry_after`, то ваши задания могут быть обработаны дважды. +> **Предупреждение**\ +> Значение `--timeout` всегда должно быть как минимум на несколько секунд короче, чем ваше значение конфигурации `retry_after`. Это гарантирует, что обрабатывающий замороженное задание обработчик, всегда завершает работу перед повторной попыткой выполнения задания. Если параметр `--timeout` выше значения конфигурации `retry_after`, то ваши задания могут быть обработаны дважды. ## Конфигурация Supervisor @@ -1586,7 +1662,8 @@ Supervisor – это диспетчер процессов для операц sudo apt-get install supervisor ``` -> {tip} Если настройка Supervisor и управление им самостоятельно кажется ошеломляющим, рассмотрите возможность использования [Laravel Forge](https://forge.laravel.com), который автоматически установит и настроит Supervisor для ваших проектов Laravel. +> **Примечание**\ +> Если настройка Supervisor и управление им самостоятельно кажется ошеломляющим, рассмотрите возможность использования [Laravel Forge](https://forge.laravel.com), который автоматически установит и настроит Supervisor для ваших проектов Laravel. #### Настройка Supervisor @@ -1610,7 +1687,8 @@ stopwaitsecs=3600 В этом примере директива `numprocs` укажет Supervisor запустить восемь процессов `queue:work` и отслеживать их все, автоматически перезапуская их в случае сбоя. Вы должны изменить директиву `command` конфигурации, чтобы отразить желаемое соединение с очередью и параметры обработчика. -> {note} Вы должны убедиться, что значение `stopwaitsecs` больше, чем количество секунд, затраченных на выполнение вашего самого продолжительного задания. В противном случае Supervisor может убить задание до того, как оно завершит обработку. +> **Предупреждение**\ +> Вы должны убедиться, что значение `stopwaitsecs` больше, чем количество секунд, затраченных на выполнение вашего самого продолжительного задания. В противном случае Supervisor может убить задание до того, как оно завершит обработку. #### Запуск Supervisor @@ -1747,7 +1825,8 @@ php artisan queue:work redis --tries=3 --backoff=3 } } -> {note} Перед вызовом метода `failed` создается новый экземпляр задания; поэтому любые изменения свойств класса, которые могли произойти в методе `handle`, будут потеряны. +> **Предупреждение**\ +> Перед вызовом метода `failed` создается новый экземпляр задания; поэтому любые изменения свойств класса, которые могли произойти в методе `handle`, будут потеряны. ### Повторная попытка выполнения неудачных заданий @@ -1788,7 +1867,8 @@ php artisan queue:retry all php artisan queue:forget 91401d2c-0784-4f43-824c-34f94a33c24d ``` -> {tip} При использовании [Horizon](horizon.md) вы должны использовать команду `horizon:forget` для удаления неудачного задания вместо команды `queue:forget`. +> **Примечание**\ +> При использовании [Horizon](horizon.md) вы должны использовать команду `horizon:forget` для удаления неудачного задания вместо команды `queue:forget`. Чтобы удалить все неудачные задания из таблицы `failed_jobs`, вы можете использовать команду `queue:flush`: @@ -1813,13 +1893,13 @@ php artisan queue:flush ### Удаление неудачных заданий -Вы можете удалить все записи в таблице `failed_jobs` вашего приложения, вызвав команду `queue:prune-failed` Artisan: +Вы можете очистить все записи в таблице `failed_jobs` вашего приложения, вызвав команду `queue:prune-failed` Artisan: ```shell php artisan queue:prune-failed ``` -Если вы укажете для команды параметр `--hours`, то будут сохранены записи о неудачных заданиях, которые были вставлены только в течение последних `N` часов. Например, следующая команда удалит все записи неудачных заданий, которые были вставлены более 48 часов назад: +По умолчанию все записи о невыполненных заданиях старше 24 часов будут удалены. Если вы укажете для команды параметр `--hours`, то будут сохранены записи о неудачных заданиях, которые были вставлены только в течение последних `N` часов. Например, следующая команда удалит все записи неудачных заданий, которые были вставлены более 48 часов назад: ```shell php artisan queue:prune-failed --hours=48 @@ -1902,7 +1982,8 @@ QUEUE_FAILED_DRIVER=null ## Удаление заданий из очередей -> {tip} При использовании [Horizon](horizon.md) вы должны использовать команду `horizon:clear` для удаления заданий из очереди вместо команды `queue:clear`. +> **Примечание**\ +> При использовании [Horizon](horizon.md) вы должны использовать команду `horizon:clear` для удаления заданий из очереди вместо команды `queue:clear`. Если вы хотите удалить все задания, принадлежащие соединению и очереди по умолчанию, вы можете сделать это с помощью команды `queue:clear` Artisan: @@ -1916,7 +1997,8 @@ php artisan queue:clear php artisan queue:clear redis --queue=emails ``` -> {note} Удаление заданий из очередей доступно только для драйверов очереди SQS, Redis и базы данных. Кроме того, процесс удаления в SQS занимает до 60 секунд, поэтому задания, отправленные в очередь SQS в течение 60 секунд после очистки очереди, также могут быть удалены. +> **Предупреждение**\ +> Удаление заданий из очередей доступно только для драйверов очереди SQS, Redis и базы данных. Кроме того, процесс удаления в SQS занимает до 60 секунд, поэтому задания, отправленные в очередь SQS в течение 60 секунд после очистки очереди, также могут быть удалены. ## Мониторинг ваших очередей diff --git a/docs/rate-limiting.md b/docs/rate-limiting.md index 6faaea0..3b8e9ef 100644 --- a/docs/rate-limiting.md +++ b/docs/rate-limiting.md @@ -11,7 +11,8 @@ Laravel включает простую в использовании абстракцию ограничения частоты, которая в сочетании с [кешем](cache.md) вашего приложения обеспечивает простой способ ограничить любое действие в течение указанного периода времени. -> {tip} Если вас интересует ограничение частоты входящих HTTP-запросов, обратитесь к [документации посредников](routing.md#rate-limiting). +> **Примечание**\ +> Если вас интересует ограничение частоты входящих HTTP-запросов, обратитесь к [документации посредников](routing.md#rate-limiting). ### Конфигурация кеша diff --git a/docs/redis.md b/docs/redis.md index 276e545..24895cb 100644 --- a/docs/redis.md +++ b/docs/redis.md @@ -264,7 +264,8 @@ composer require predis/predis $redis->incr('total_visits', 1); }); -> {note} При определении транзакции Redis вы не можете получать какие-либо значения из соединения Redis. Помните, ваша транзакция выполняется как одна атомарная операция, и эта операция не выполнится, пока не завершится выполнение всех команд замыкания. +> **Предупреждение**\ +> При определении транзакции Redis вы не можете получать какие-либо значения из соединения Redis. Помните, ваша транзакция выполняется как одна атомарная операция, и эта операция не выполнится, пока не завершится выполнение всех команд замыкания. #### Скрипты Lua @@ -284,7 +285,8 @@ composer require predis/predis return counter LUA, 2, 'first-counter', 'second-counter'); -> {note} Пожалуйста, обратитесь к [документации Redis](https://redis.io/commands/eval) для получения дополнительных сведений о сценариях Redis. +> **Предупреждение**\ +> Пожалуйста, обратитесь к [документации Redis](https://redis.io/commands/eval) для получения дополнительных сведений о сценариях Redis. ### Конвейерное выполнение команд diff --git a/docs/releases.md b/docs/releases.md index d538560..45022f0 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -26,8 +26,8 @@ Laravel и другие его собственные пакеты следую | 6 (LTS) | 7.2 - 8.0 | 3 сентября 2019 | 25 января 2022 | 6 сентября 2022 | | 7 | 7.2 - 8.0 | 3 марта 2020 | 6 октября 2020 | 3 марта 2021 | | 8 | 7.3 - 8.1 | 8 сентября 2020 | 26 июля 2022 | 24 января 2023 | -| 9 | 8.0 - 8.1 | 8 февраля 2022 | 8 августа 2023 | 8 февраля 2024 | -| 10 | 8.1 | 7 февраля 2023 | 7 августа 2024 | 7 февраля 2025 | +| 9 | 8.0 - 8.2 | 8 февраля 2022 | 8 августа 2023 | 6 февраля 2024 | +| 10 | 8.1 - 8.2 | 7 февраля 2023 | 6 августа 2024 | 4 февраля 2025 | @@ -260,7 +264,8 @@ Sanctum также обеспечивает простой метод аутен Во-первых, вы должны настроить, из каких доменов ваш SPA будет делать запросы. Вы можете указать эти домены, используя параметр `stateful` в конфигурационном файле `sanctum`. Этот параметр конфигурации определяет, какие домены будут поддерживать аутентификацию с «фиксацией» на основе файлов cookie сессии Laravel при выполнении запросов к вашему API. -> {note} Если вы обращаетесь к своему приложению через URL-адрес, содержащий порт (например, `127.0.0.1:8000`), то вы должны убедиться, что указали номер порта вместе с доменом. +> **Предупреждение**\ +> Если вы обращаетесь к своему приложению через URL-адрес, содержащий порт (например, `127.0.0.1:8000`), то вы должны убедиться, что указали номер порта вместе с доменом. #### Посредник Sanctum @@ -315,7 +320,8 @@ axios.get('/sanctum/csrf-cookie').then(response => { Конечно, если сессия вашего пользователя истекает из-за отсутствия активности, то последующие запросы к приложению Laravel могут получить ответ об ошибках `401` или `419` HTTP. В этом случае вы должны перенаправить пользователя на страницу входа в SPA. -> {note} Вы можете написать свою собственную конечную точку `/login`; однако, вы должны убедиться, что он аутентифицирует пользователя, используя стандартные [службы аутентификации на основе сессии](authentication.md#authenticating-users), которые предлагает Laravel. Обычно это означает использование охранника аутентификации `web`. +> **Предупреждение**\ +> Вы можете написать свою собственную конечную точку `/login`; однако, вы должны убедиться, что он аутентифицирует пользователя, используя стандартные [службы аутентификации на основе сессии](authentication.md#authenticating-users), которые предлагает Laravel. Обычно это означает использование охранника аутентификации `web`. ### Защита маршрутов SPA @@ -340,9 +346,9 @@ axios.get('/sanctum/csrf-cookie').then(response => { ```js window.Echo = new Echo({ broadcaster: "pusher", - cluster: process.env.MIX_PUSHER_APP_CLUSTER, + cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, encrypted: true, - key: process.env.MIX_PUSHER_APP_KEY, + key: import.meta.env.VITE_PUSHER_APP_KEY, authorizer: (channel, options) => { return { authorize: (socketId, callback) => { @@ -399,7 +405,8 @@ window.Echo = new Echo({ Когда мобильное приложение использует токен для запроса API к вашему приложению, оно должно передать токен в заголовке `Authorization` как `Bearer`-токен. -> {tip} При выдаче токенов для мобильного приложения вы также можете указать [полномочия токена](#token-abilities). +> **Примечание**\ +> При выдаче токенов для мобильного приложения вы также можете указать [полномочия токена](#token-abilities). ### Защита маршрутов API мобильного приложения diff --git a/docs/scheduling.md b/docs/scheduling.md index 6f94936..55e7daf 100644 --- a/docs/scheduling.md +++ b/docs/scheduling.md @@ -117,6 +117,7 @@ php artisan schedule:list `->everyThirtyMinutes();` | – каждые 30 минут `->hourly();` | – каждый час `->hourlyAt(17);` | – в 17 минут каждого часа +`->everyOddHour();` | – каждый нечетный час `->everyTwoHours();` | – каждые 2 часа `->everyThreeHours();` | – каждые 3 часа `->everyFourHours();` | – каждые 4 часа @@ -124,6 +125,7 @@ php artisan schedule:list `->daily();` | – каждый день в полночь `->dailyAt('13:00');` | – ежедневно в 13:00 `->twiceDaily(1, 13);` | – ежедневно дважды в день: в 1:00 и 13:00 +`->twiceDailyAt(1, 13, 15);` | – ежедневно в 1:15 и 13:15 `->weekly();` | – еженедельно в воскресенье в 00:00 `->weeklyOn(1, '8:00');` | – еженедельно в понедельник в 8:00 `->monthly();` | – ежемесячно первого числа в 00:00 @@ -131,6 +133,7 @@ php artisan schedule:list `->twiceMonthly(1, 16, '13:00');` | – ежемесячно дважды в месяц: 1 и 16 числа в 13:00 `->lastDayOfMonth('15:00');` | – ежемесячно в последний день месяца в 15:00 `->quarterly();` | – ежеквартально в первый день в 00:00 +`->quarterlyOn(4, '14:00');` | – ежеквартально 4 числа в 14:00 `->yearly();` | – ежегодно в первый день в 00:00 `->yearlyOn(6, 1, '17:00');` | – ежегодно в июне первого числа в 17:00 `->timezone('America/New_York');` | Установить часовой пояс для задачи @@ -247,7 +250,8 @@ php artisan schedule:list return 'America/Chicago'; } -> {note} Помните, что в некоторых часовых поясах используется летнее время. Когда происходит переход на летнее время, ваша запланированная задача может запускаться дважды или даже не запускаться вообще. По этой причине мы рекомендуем по возможности избегать указаний часовых поясов при планировании. +> **Предупреждение**\ +> Помните, что в некоторых часовых поясах используется летнее время. Когда происходит переход на летнее время, ваша запланированная задача может запускаться дважды или даже не запускаться вообще. По этой причине мы рекомендуем по возможности избегать указаний часовых поясов при планировании. ### Предотвращение дублирования задач @@ -267,7 +271,8 @@ php artisan schedule:list ### Выполнение задач на одном сервере -> {note} Чтобы использовать этот функционал, ваше приложение должно использовать по умолчанию один из следующих драйверов кеша: `database`, `memcached`, `dynamodb` или `redis`. Кроме того, все серверы должны взаимодействовать с одним и тем же центральным сервером кеширования. +> **Предупреждение**\ +> Чтобы использовать этот функционал, ваше приложение должно использовать по умолчанию один из следующих драйверов кеша: `database`, `memcached`, `dynamodb` или `redis`. Кроме того, все серверы должны взаимодействовать с одним и тем же центральным сервером кеширования. Если планировщик вашего приложения работает на нескольких серверах, то вы можете ограничить выполнение запланированного задания только на одном сервере. Например, предположим, что у вас есть запланированная задача, по которой каждую пятницу вечером создается новый отчет. Если планировщик задач работает на трех рабочих серверах, запланированная задача будет запущена на всех трех серверах и трижды сгенерирует отчет. Не очень хорошо! @@ -278,6 +283,33 @@ php artisan schedule:list ->at('17:00') ->onOneServer(); + +#### Именование заданий на одном сервере + +Иногда требуется запланировать отправку одного и того же задания с разными параметрами, при этом указывая Laravel выполнять каждое задание на одном сервере. Для этого вы можете присвоить каждому определению расписания уникальное имя с помощью метода `name`: + +```php +$schedule->job(new CheckUptime('https://laravel.com')) + ->name('check_uptime:laravel.com') + ->everyFiveMinutes() + ->onOneServer(); + +$schedule->job(new CheckUptime('https://vapor.laravel.com')) + ->name('check_uptime:vapor.laravel.com') + ->everyFiveMinutes() + ->onOneServer(); +``` + +Точно так же анонимным заданиям должно быть присвоено имя, если они предназначены для запуска на одном сервере: + +```php +$schedule->call(fn () => User::resetApiRequestCount()) + ->name('reset-api-request-count') + ->daily() + ->onOneServer(); +``` + + ### Фоновые задачи @@ -287,7 +319,8 @@ php artisan schedule:list ->daily() ->runInBackground(); -> {note} Метод `runInBackground` может использоваться только при планировании задач с помощью методов `command` и `exec`. +> **Предупреждение**\ +> Метод `runInBackground` может использоваться только при планировании задач с помощью методов `command` и `exec`. ### Режим технического обслуживания @@ -344,7 +377,8 @@ php artisan schedule:work ->daily() ->emailOutputOnFailure('taylor@example.com'); -> {note} Методы `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo`, and `appendOutputTo` могут использоваться только при планировании задач с помощью методов `command` и `exec`. +> **Предупреждение**\ +> Методы `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo` и `appendOutputTo` могут использоваться только при планировании задач с помощью методов `command` и `exec`. ## Хуки выполнения задачи diff --git a/docs/scout.md b/docs/scout.md index d8cfc08..a042472 100644 --- a/docs/scout.md +++ b/docs/scout.md @@ -99,7 +99,8 @@ MEILISEARCH_KEY=masterKey Кроме того, вам следует убедиться, что вы установили версию `meilisearch/meilisearch-php`, совместимую с вашей версией MeiliSearch, просмотрев [документацию MeiliSearch о совместимости](https://github.com/meilisearch/meilisearch-php#-compatibility-with-meilisearch). -> {note} При обновлении Scout в приложении, которое использует MeiliSearch, вы всегда должны [просматривать любые дополнительные критические изменения](https://github.com/meilisearch/MeiliSearch/releases) в самой службе MeiliSearch. +> **Предупреждение**\ +> При обновлении Scout в приложении, которое использует MeiliSearch, вы всегда должны [просматривать любые дополнительные критические изменения](https://github.com/meilisearch/MeiliSearch/releases) в самой службе MeiliSearch. ### Постановка в очередь @@ -112,6 +113,13 @@ MEILISEARCH_KEY=masterKey Даже когда для параметра `queue` установлено значение `false`, важно помнить, что некоторые драйверы Scout, такие как Algolia и MeiliSearch, всегда индексируют записи асинхронно. Это означает, что даже если операция индексирования завершена в вашем приложении Laravel, то сама поисковая система может не сразу отображать новые и обновленные записи. +Чтобы указать соединение и очередь, которые используют ваши задания Scout, вы можете определить параметр конфигурации `queue` как массив: + + 'queue' => [ + 'connection' => 'redis', + 'queue' => 'scout' + ], + ## Конфигурирование @@ -173,6 +181,59 @@ MEILISEARCH_KEY=masterKey } } +Некоторые поисковые системы, такие как MeiliSearch, будут выполнять операции фильтрации (`>`, `<` и т. д.) только для корректных данных. Итак, при использовании этих поисковых систем и настройке поисковых данных вы должны убедиться, что числовые значения приведены к корректному типу: + + public function toSearchableArray() + { + return [ + 'id' => (int) $this->id, + 'name' => $this->name, + 'price' => (float) $this->price, + ]; + } + + +#### Настройка фильтруемых данных и параметров индекса (MeiliSearch) + +В отличие от других драйверов Scout, MeiliSearch требует, чтобы вы предварительно определили параметры поиска по индексу, такие как фильтруемые, сортируемые атрибуты и [другие поддерживаемые поля настроек](https://docs.meilisearch.com/reference/api/settings.html). + +Фильтруемые атрибуты — это любые атрибуты, которые вы планируете фильтровать при вызове метода `where` Scout, а сортируемые атрибуты – это любые атрибуты, по которым вы планируете сортировать при вызове метода `orderBy` Scout. Чтобы определить параметры индекса, настройте параметр `index-settings` в конфигурации `meilisearch` в конфигурационном файле `scout` вашего приложения: + +```php +use App\Models\User; +use App\Models\Flight; + +'meilisearch' => [ + 'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'), + 'key' => env('MEILISEARCH_KEY', null), + 'index-settings' => [ + User::class => [ + 'filterableAttributes'=> ['id', 'name', 'email'], + 'sortableAttributes' => ['created_at'], + // Другие поля настроек ... + ], + Flight::class => [ + 'filterableAttributes'=> ['id', 'destination'], + 'sortableAttributes' => ['updated_at'], + ], + ], +], +``` + +Если индексируемая модель допускает программное удаление и включена в массив `index-settings`, Scout автоматически включит поддержку фильтрации программно удаляемых моделей в этом индексе. Если у вас нет других фильтруемых или сортируемых атрибутов для определения индекса программно удаляемой модели, то вы можете просто добавить пустую запись в массив `index-settings` для этой модели: + +```php +'index-settings' => [ + Flight::class => [] +], +``` + +После настройки параметров индекса вашего приложения вы должны вызвать команду `scout:sync-index-settings` Artisan. Эта команда сообщит MeiliSearch о ваших текущих настройках индекса. Для удобства вы можете сделать эту команду частью процесса развертывания: + +```shell +php artisan scout:sync-index-settings +``` + ### Настройка идентификатора модели @@ -255,7 +316,8 @@ SCOUT_IDENTIFY=true ### Поисковая система `database` -> {note} Поисковая система `database` в настоящее время поддерживает MySQL и PostgreSQL. +> **Предупреждение**\ +> Поисковая система `database` в настоящее время поддерживает MySQL и PostgreSQL. Если ваше приложение взаимодействует с базами данных малого и среднего размера или имеет небольшую рабочую нагрузку, то вам может быть удобнее начать работу с поисковой системой `database` Scout. Поисковая система базы данных будет использовать выражения `where like` и полнотекстовые индексы при фильтрации результатов из вашей существующей базы данных, чтобы определить применимые результаты поиска для вашего запроса. @@ -295,7 +357,8 @@ public function toSearchableArray() } ``` -> {note} Прежде чем указать, что столбец должен использовать ограничения полнотекстового запроса, убедитесь, что столбцу назначен [полнотекстовый индекс](migrations.ms#available-index-types). +> **Предупреждение**\ +> Прежде чем указать, что столбец должен использовать ограничения полнотекстового запроса, убедитесь, что столбцу назначен [полнотекстовый индекс](migrations.ms#available-index-types). ### Поисковая система `collection` @@ -380,7 +443,8 @@ php artisan scout:flush "App\Models\Post" $orders->searchable(); -> {tip} Метод `searchable` можно считать операцией «обновления-вставки». Другими словами, если запись модели уже есть в вашем индексе, то она будет обновлена. Если записи нет в поисковом индексе, то она будет добавлен в индекс. +> **Примечание**\ +> Метод `searchable` можно считать операцией «обновления-вставки». Другими словами, если запись модели уже есть в вашем индексе, то она будет обновлена. Если записи нет в поисковом индексе, то она будет добавлен в индекс. ### Обновление записей @@ -458,7 +522,8 @@ php artisan scout:flush "App\Models\Post" Метод `shouldBeSearchable` учитывается при манипулировании моделями через запросы, отношения или методы `save` и `create`. Непосредственное указание доступности моделей или коллекций для поиска с помощью метода `searchable` переопределит результат метода `shouldBeSearchable`. -> {note} Метод `shouldBeSearchable` неприменим при использовании поисковой системы `database` Scout, поскольку все доступные для поиска данные всегда хранятся в базе данных. Чтобы добиться аналогичного поведения при использовании поисковой системы `database`, вместо этого следует использовать [выражения `where`](#where-clauses). +> **Предупреждение**\ +> Метод `shouldBeSearchable` неприменим при использовании поисковой системы `database` Scout, поскольку все доступные для поиска данные всегда хранятся в базе данных. Чтобы добиться аналогичного поведения при использовании поисковой системы `database`, вместо этого следует использовать [выражения `where`](#where-clauses). ## Поиск @@ -508,6 +573,9 @@ Scout позволяет добавлять к поисковым запроса Поскольку поисковый индекс не является реляционной базой данных, более сложные выражения `WHERE` в настоящее время не поддерживаются. +> **Предупреждение**\ +> Если ваше приложение использует MeiliSearch, то вы должны настроить [фильтруемые атрибуты](#configuring-filterable-data-for-meilisearch) вашего приложения, прежде чем использовать выражения `where` Scout. + ### Постраничная навигация @@ -542,6 +610,9 @@ Scout позволяет добавлять к поисковым запроса return Order::search($request->input('query'))->paginate(15); }); +> **Предупреждение**\ +> Поскольку поисковые системы не знают об определениях [глобальных областей запроса](eloquent.md#global-scopes) вашей модели Eloquent, вам не следует использовать глобальные области в приложениях, использующих разбиение на страницы Scout. Или вам следует воссоздать ограничения глобальной области запроса при поиске через Scout. + ### Программное удаление @@ -559,7 +630,8 @@ Scout позволяет добавлять к поисковым запроса // Вывести только удаленные записи при получении результатов ... $orders = Order::search('Star Trek')->onlyTrashed()->get(); -> {tip} Если модель будет окончательно удалена с помощью `forceDelete`, то Scout автоматически удалит ее из поискового индекса. +> **Примечание**\ +> Если модель будет окончательно удалена с помощью `forceDelete`, то Scout автоматически удалит ее из поискового индекса. ### Изменение техники поиска diff --git a/docs/seeding.md b/docs/seeding.md index 37723ca..532c784 100644 --- a/docs/seeding.md +++ b/docs/seeding.md @@ -12,7 +12,8 @@ Laravel предлагает возможность наполнения базы данных с использованием классов-наполнителей. Все классы наполнителей хранятся в каталоге `database/seeders`. Класс `DatabaseSeeder` уже определен по умолчанию. В этом классе вы можете использовать метод `call` для запуска других наполнителей, что позволит вам контролировать порядок наполнения БД. -> {tip} При наполнении базы данных автоматически отключается защита [массового присвоения](eloquent.md#mass-assignment). +> **Примечание**\ +> При наполнении базы данных автоматически отключается защита [массового присвоения](eloquent.md#mass-assignment). ## Написание наполнителей @@ -23,7 +24,7 @@ Laravel предлагает возможность наполнения баз php artisan make:seeder UserSeeder ``` -Класс наполнителя по умолчанию содержит только один метод: `run`. Этот метод вызывается при выполнении команды `db:seed` Artisan. В методе `run` вы можете вставлять данные в свою базу данных, как хотите. Вы можете использовать [построитель запросов](queries.md) для самостоятельной вставки данных или использовать [фабрики моделей Eloquent](database-testing.md#defining-model-factories). +Класс наполнителя по умолчанию содержит только один метод: `run`. Этот метод вызывается при выполнении команды `db:seed` Artisan. В методе `run` вы можете вставлять данные в свою базу данных, как хотите. Вы можете использовать [построитель запросов](queries.md) для самостоятельной вставки данных или использовать [фабрики моделей Eloquent](eloquent-factories.md). В качестве примера давайте изменим класс `DatabaseSeeder`, созданный по умолчанию, и добавим выражение вставки фасада `DB` в методе `run`: @@ -53,12 +54,13 @@ php artisan make:seeder UserSeeder } } -> {tip} В методе `run` вы можете объявить любые необходимые типы зависимостей. Они будут автоматически извлечены и внедрены через [контейнер служб](container.md) Laravel. +> **Примечание**\ +> В методе `run` вы можете объявить любые необходимые типы зависимостей. Они будут автоматически извлечены и внедрены через [контейнер служб](container.md) Laravel. ### Использование фабрик моделей -Конечно, ручное указание атрибутов для каждой модели наполнителя обременительно. Вместо этого вы можете использовать [фабрики моделей](database-testing.md#defining-model-factories) для удобного создания большого количества записей в БД. Сначала просмотрите [документацию фабрики моделей](database-testing.md#defining-model-factories), чтобы узнать, как определить свои фабрики. +Конечно, ручное указание атрибутов для каждой модели наполнителя обременительно. Вместо этого вы можете использовать [фабрики моделей](eloquent-factories.md) для удобного создания большого количества записей в БД. Сначала просмотрите [документацию фабрики моделей](eloquent-factories.md), чтобы узнать, как определить свои фабрики. Например, давайте создадим 50 пользователей, у каждого из которых будет по одному посту: @@ -136,10 +138,12 @@ php artisan db:seed php artisan db:seed --class=UserSeeder ``` -Вы также можете заполнить свою базу данных с помощью команды `migrate:fresh` в сочетании с параметром `--seed`, которая удалит все таблицы и повторно запустит все ваши миграции. Эта команда полезна для полного перестроения вашей базы данных: +Вы также можете заполнить свою базу данных с помощью команды `migrate:fresh` в сочетании с параметром `--seed`, которая удалит все таблицы и повторно запустит все ваши миграции. Эта команда полезна для полного перестроения вашей базы данных. Параметр `--seeder` может использоваться для указания конкретного наполнителя для запуска: ```shell php artisan migrate:fresh --seed + +php artisan migrate:fresh --seed --seeder=UserSeeder ``` diff --git a/docs/session.md b/docs/session.md index 6e87620..ed6263e 100644 --- a/docs/session.md +++ b/docs/session.md @@ -39,7 +39,8 @@ Laravel предлагает множество различных типов х -> {tip} Драйвер `array` в основном используется во время [тестирования](testing.md) и предотвращает сохранение данных, находящихся в сессии. +> **Примечание**\ +> Драйвер `array` в основном используется во время [тестирования](testing.md) и предотвращает сохранение данных, находящихся в сессии. ### Предварительная подготовка драйверов @@ -71,7 +72,8 @@ php artisan migrate Перед использованием Redis с Laravel вам нужно будет либо установить расширение PHP PhpRedis через PECL, либо установить пакет `predis/predis` (~ 1.0) через Composer. Для получения дополнительной информации о настройке Redis обратитесь к [документации Redis](redis.md#configuration) Laravel. -> {tip} В параметре `connection` конфигурационного файла `config/session.php` указывается, какое соединение Redis используется сессией. +> **Примечание**\ +> В параметре `connection` конфигурационного файла `config/session.php` указывается, какое соединение Redis используется сессией. ## Взаимодействие с сессией @@ -129,7 +131,8 @@ php artisan migrate session(['key' => 'value']); }); -> {tip} Существует небольшая практическая разница между использованием сессии через экземпляр HTTP-запроса и использованием глобального помощника `session`. Оба метода [тестируемые](testing.md) с помощью метода `assertSessionHas`, который доступен во всех ваших тестах. +> **Примечание**\ +> Существует небольшая практическая разница между использованием сессии через экземпляр HTTP-запроса и использованием глобального помощника `session`. Оба метода [тестируемые](testing.md) с помощью метода `assertSessionHas`, который доступен во всех ваших тестах. #### Получение всех данных сессии @@ -153,7 +156,7 @@ php artisan migrate // } -Чтобы определить отсутствие элемента в сессии, вы можете использовать метод `missing`. Метод `missing` возвращает `true`, если элемент отсутствует или если он равен `null`: +Чтобы определить отсутствие элемента в сессии, вы можете использовать метод `missing`. Метод `missing` возвращает `true`, если элемент отсутствует: if ($request->session()->missing('users')) { // @@ -243,7 +246,8 @@ Laravel автоматически пересоздает идентификат ## Блокировка сессии -> {note} Чтобы использовать блокировку сессии, ваше приложение должно использовать драйвер кеша с поддержкой [атомарных блокировок](cache.md#atomic-locks). В настоящее время этими драйверами кеширования являются `memcached`, `dynamodb`, `redis` и `database`. Кроме того, вы не можете использовать драйвер сессии `cookie`. +> **Предупреждение**\ +> Чтобы использовать блокировку сессии, ваше приложение должно использовать драйвер кеша с поддержкой [атомарных блокировок](cache.md#atomic-locks). В настоящее время этими драйверами кеширования являются `memcached`, `dynamodb`, `redis` и `database`. Кроме того, вы не можете использовать драйвер сессии `cookie`. По умолчанию Laravel позволяет конкурентное выполнение запросов, использующих одну и ту же сессию. Так, например, если вы используете HTTP-библиотеку JavaScript для выполнения двух HTTP-запросов к вашему приложению, то они оба будут выполняться одновременно. Для многих приложений это не проблема; однако в некоторых приложениях, выполняющих запросы с записью данных в сессию и к двум различным конечным точкам приложения, может произойти потеря данных сессии. @@ -289,7 +293,8 @@ Laravel автоматически пересоздает идентификат public function gc($lifetime) {} } -> {tip} Laravel не содержит каталога для хранения ваших расширений. Вы можете разместить их где угодно. В этом примере мы создали каталог `Extensions` для размещения `MongoSessionHandler`. +> **Примечание**\ +> Laravel не содержит каталога для хранения ваших расширений. Вы можете разместить их где угодно. В этом примере мы создали каталог `Extensions` для размещения `MongoSessionHandler`. Поскольку цель этих методов не совсем понятна, давайте быстро рассмотрим, что делает каждый из этих методов: diff --git a/docs/socialite.md b/docs/socialite.md index c29b7e4..b5dcb27 100644 --- a/docs/socialite.md +++ b/docs/socialite.md @@ -16,7 +16,8 @@ Помимо типичной аутентификации на основе форм, Laravel также предлагает простой и удобный способ аутентификации через провайдеров OAuth с помощью [Laravel Socialite](https://github.com/laravel/socialite). Socialite в настоящее время поддерживает аутентификацию через Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, и Bitbucket. -> {tip} Адаптеры для других платформ перечислены на веб-сайте [Socialite Providers](https://socialiteproviders.com/), управляемом сообществом. +> **Примечание**\ +> Адаптеры для других платформ перечислены на веб-сайте [Socialite Providers](https://socialiteproviders.com/), управляемом сообществом. ## Установка @@ -45,7 +46,8 @@ composer require laravel/socialite 'redirect' => 'http://example.com/callback-url', ], -> {tip} Если параметр `redirect` содержит относительный путь, то он будет автоматически преобразован в абсолютный URL. +> **Примечание**\ +> Если параметр `redirect` содержит относительный путь, то он будет автоматически преобразован в абсолютный URL. ## Аутентификация @@ -95,7 +97,8 @@ composer require laravel/socialite return redirect('/dashboard'); }); -> {tip} О том, какая информация о пользователе доступна у конкретных провайдеров OAuth, ознакомьтесь с документацией по [получению сведений о пользователе](#retrieving-user-details). +> **Примечание**\ +> О том, какая информация о пользователе доступна у конкретных провайдеров OAuth, ознакомьтесь с документацией по [получению сведений о пользователе](#retrieving-user-details). ### Права доступа @@ -125,7 +128,8 @@ composer require laravel/socialite ->with(['hd' => 'example.com']) ->redirect(); -> {note} При использовании метода `with` будьте осторожны, чтобы не передавать какие-либо зарезервированные ключевые слова, такие как `state` или `response_type`. +> **Предупреждение**\ +> При использовании метода `with` будьте осторожны, чтобы не передавать какие-либо зарезервированные ключевые слова, такие как `state` или `response_type`. ## Получение сведений о пользователе @@ -183,4 +187,5 @@ composer require laravel/socialite return Socialite::driver('google')->stateless()->user(); -> {note} Аутентификация без сохранения состояния недоступна для драйвера Twitter OAuth 1.0. +> **Предупреждение**\ +> Аутентификация без сохранения состояния недоступна для драйвера Twitter OAuth 1.0. diff --git a/docs/starter-kits.md b/docs/starter-kits.md index 94b2fcf..27d9345 100644 --- a/docs/starter-kits.md +++ b/docs/starter-kits.md @@ -3,6 +3,7 @@ - [Введение](#introduction) - [Laravel Breeze](#laravel-breeze) - [Установка](#laravel-breeze-installation) + - [Breeze и Blade](#breeze-and-blade) - [Breeze и React / Vue](#breeze-and-inertia) - [Breeze и Next.js / API](#breeze-and-next) - [Laravel Jetstream](#laravel-jetstream) @@ -17,51 +18,64 @@ ## Laravel Breeze -[Laravel Breeze](https://github.com/laravel/breeze) – это минимальная и простая реализация всего [функционала аутентификации](authentication.md) Laravel, включая вход в систему, регистрацию, сброс пароля, подтверждение адреса электронной почты и пароля. Слой «View» комплекта Laravel Breeze по умолчанию состоит из простых [шаблонов Blade](blade.md), стилизованных с помощью [Tailwind CSS](https://tailwindcss.com). +[Laravel Breeze](https://github.com/laravel/breeze) – это минимальная и простая реализация всего [функционала аутентификации](authentication.md) Laravel, включая вход в систему, регистрацию, сброс пароля, подтверждение адреса электронной почты и пароля. Кроме того, Breeze включает простую страницу «профиля», где пользователь может обновить свое имя, адрес электронной почты и пароль. + +Слой «View» комплекта Laravel Breeze по умолчанию состоит из простых [шаблонов Blade](blade.md), стилизованных с помощью [Tailwind CSS](https://tailwindcss.com). Или Breeze может создать каркас вашего приложения с помощью Vue или React и [Inertia] (https://inertiajs.com). Breeze является прекрасной отправной точкой для создания нового приложения Laravel, а также отличный выбор для проектов, которые планируют вывести использование шаблонов Blade на новый уровень с помощью [Laravel Livewire](https://laravel-livewire.com). +#### Laravel Bootcamp + +Если вы новичок в Laravel, не стесняйтесь перейти в [Laravel Bootcamp] (https://bootcamp.laravel.com). Laravel Bootcamp проведет вас через создание вашего первого приложения Laravel с использованием Breeze. Это отличный способ познакомиться со всем, что могут предложить Laravel и Breeze. + ### Установка -Сначала вы должны [создать новое приложение Laravel](installation.md), настроить свою базу данных и запустить [миграции базы данных](migrations.md): +Сначала вы должны [создать новое приложение Laravel](installation.md), настроить свою базу данных и запустить [миграции базы данных](migrations.md). После того, как вы создали новое приложение Laravel, вы можете установить Laravel Breeze с помощью Composer: ```shell -curl -s https://laravel.build/example-app | bash - -cd example-app - -php artisan migrate +composer require laravel/breeze --dev ``` -Создав новое приложение Laravel, вы можете установить Laravel Breeze с помощью Composer: +После установки Breeze вы можете создать шаблон для своего приложения, используя один из «стеков» Breeze, описанных в документации ниже. -```shell -composer require laravel/breeze --dev -``` + +### Breeze и Blade + +После того, как Composer установит пакет Laravel Breeze, вы можете запустить команду `breeze:install` Artisan. Эта команда опубликует для вашего приложения шаблоны, маршруты, контроллеры и другие ресурсы аутентификации. Laravel Breeze опубликует весь свой код в вашем приложении, чтобы у вас был полный контроль, а также обзор всего функционала и его реализации. -После того, как Composer установит пакет Laravel Breeze, вы можете запустить команду `breeze:install` Artisan. Эта команда опубликует для вашего приложения шаблоны, маршруты, контроллеры и другие ресурсы аутентификации. Laravel Breeze опубликует весь свой код в вашем приложении, чтобы у вас был полный контроль, а также обзор всего функционала и его реализации. После установки Breeze вы также должны скомпилировать свои исходники, чтобы был доступен файл стилей вашего приложения: +Стек Breeze по умолчанию — это стек Blade, который использует простые [шаблоны Blade](blade.md) для отрисовки внешнего интерфейса вашего приложения. Стек Blade можно установить, вызвав команду `breeze:install` без дополнительных аргументов. После установки каркаса Breeze вы также должны скомпилировать ресурсы внешнего интерфейсные вашего приложения: ```shell php artisan breeze:install +php artisan migrate npm install npm run dev -php artisan migrate ``` Затем, вы можете перейти в своем веб-браузере по URL-адресам вашего приложения `/login` или `/register`. Все маршруты Breeze определены в файле `routes/auth.php`. -> {tip} Чтобы узнать больше о компиляции CSS и JavaScript вашего приложения, ознакомьтесь с [документацией Laravel Mix](mix.md#running-mix). + +#### Темная тема + +Если вы хотите, чтобы Breeze включил поддержку «темной темы» при создании внешнего интерфейса вашего приложения, просто укажите директиву `--dark` при выполнении команды `breeze:install`: + +```shell +php artisan breeze:install --dark +``` + +> **Примечание**\ +> Чтобы узнать больше о компиляции CSS и JavaScript вашего приложения, ознакомьтесь с [документацией Laravel Vite](vite.md#running-vite). ### Breeze и React / Vue -Laravel Breeze также предлагает каркасы React и Vue через реализацию внешнего интерфейса [Inertia.js](https://inertiajs.com). Inertia позволяет создавать современные одностраничные приложения React и Vue, используя классическую маршрутизацию и контроллеры на стороне сервера. +Laravel Breeze также предлагает каркасы React и Vue через реализацию внешнего интерфейса [Inertia](https://inertiajs.com). Inertia позволяет создавать современные одностраничные приложения React и Vue, используя классическую маршрутизацию и контроллеры на стороне сервера. -Inertia позволяет вам наслаждаться мощью внешнего интерфейса React и Vue в сочетании с невероятной производительностью Laravel. Чтобы использовать стек Inertia, укажите `vue` или `react` в качестве желаемого стека при выполнении команды `breeze:install` Artisan: +Inertia позволяет вам наслаждаться мощью внешнего интерфейса React и Vue в сочетании с невероятной производительностью Laravel и молниеносной компиляцией [Vite](https://vitejs.dev). Чтобы использовать стек Inertia, укажите `vue` или `react` в качестве желаемого стека при выполнении команды `breeze:install` Artisan. После установки каркаса Breeze вы также должны скомпилировать ресурсы внешнего интерфейсные вашего приложения: ```shell php artisan breeze:install vue @@ -70,11 +84,16 @@ php artisan breeze:install vue php artisan breeze:install react +php artisan migrate npm install npm run dev -php artisan migrate ``` +Затем, вы можете перейти в своем веб-браузере по URL-адресам вашего приложения `/login` или `/register`. Все маршруты Breeze определены в файле `routes/auth.php`. + + +#### Серверный рендеринг + Если вы хотите, чтобы Breeze поддерживал [Inertia SSR](https://inertiajs.com/server-side-rendering), то вы можете указать параметр `ssr` при вызове команды `breeze:install`: ```shell @@ -105,6 +124,6 @@ php artisan migrate В то время как Laravel Breeze обеспечивает простую и минимальную отправную точку для создания приложения Laravel, Jetstream дополняет эту функциональность более надежными функциями и дополнительными стеками технологий клиентского интерфейса. **Для тех, кто новичок в Laravel, мы рекомендуем изучить основы работы с Laravel Breeze перед тем, как перейти на Laravel Jetstream.** -Jetstream предлагает красиво оформленный каркас приложений для Laravel и включает в себя вход в систему, регистрацию, подтверждение адреса электронной почты, двухфакторную аутентификацию, управление сессиями, поддержку API через Laravel Sanctum, и дополнительно, управление командой. Jetstream разработан с использованием [Tailwind CSS](https://tailwindcss.com) и предлагает на ваш выбор каркас клиентского интерфейса под управлением [Livewire](https://laravel-livewire.com) либо [Inertia.js](https://inertiajs.com). +Jetstream предлагает красиво оформленный каркас приложений для Laravel и включает в себя вход в систему, регистрацию, подтверждение адреса электронной почты, двухфакторную аутентификацию, управление сессиями, поддержку API через Laravel Sanctum, и дополнительно, управление командой. Jetstream разработан с использованием [Tailwind CSS](https://tailwindcss.com) и предлагает на ваш выбор каркас клиентского интерфейса под управлением [Livewire](https://laravel-livewire.com) либо [Inertia](https://inertiajs.com). Полное описание по установке Laravel Jetstream можно найти в [официальной документации Jetstream](https://jetstream.laravel.com). diff --git a/docs/structure.md b/docs/structure.md index cb2dbd8..e147504 100644 --- a/docs/structure.md +++ b/docs/structure.md @@ -33,6 +33,9 @@ Структура приложения Laravel по умолчанию предназначена для обеспечения отличной отправной точки как для больших, так и небольших приложений. Но вы можете организовать свое приложение так, как вам нравится. Laravel почти не налагает ограничений на расположение любого конкретного класса, до тех пор, пока Composer может автоматически загружать класс. +> **Примечание**\ +> Новичок в Laravel? Посетите [Laravel Bootcamp](https://bootcamp.laravel.com) для практического ознакомления с фреймворком, пока мы познакомим вас с созданием вашего первого приложения Laravel. + ## Корневой каталог @@ -110,7 +113,8 @@ Множество других каталогов будет создано внутри каталога `app`, когда вы будете использовать команды `make` Artisan для создания классов. Так, например, каталог `app/Jobs` не будет существовать, пока вы не выполните команду `make:job` Artisan для создания класса задания. -> {tip} Многие классы в каталоге `app` могут быть созданы с помощью команд Artisan. Чтобы просмотреть доступные команды, выполните команду `php artisan list make` в консоли. +> **Примечание**\ +> Многие классы в каталоге `app` могут быть созданы с помощью команд Artisan. Чтобы просмотреть доступные команды, выполните команду `php artisan list make` в консоли. #### Каталог пространства `Broadcasting` diff --git a/docs/telescope.md b/docs/telescope.md index 3b0b8ef..788d051 100644 --- a/docs/telescope.md +++ b/docs/telescope.md @@ -142,7 +142,8 @@ php artisan migrate }); } -> {note} Убедитесь, что вы изменили значение переменной `APP_ENV` на `production` в эксплуатационном окружении. В противном случае доступ к Telescope будет публичным. +> **Предупреждение**\ +> Убедитесь, что вы изменили значение переменной `APP_ENV` на `production` в эксплуатационном окружении. В противном случае доступ к Telescope будет публичным. ## Обновление пакета Telescope diff --git a/docs/testing.md b/docs/testing.md index 6b3a81d..5c1e7ab 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -57,7 +57,8 @@ php artisan make:test UserTest --pest php artisan make:test UserTest --unit --pest ``` -> {tip} Заготовки тестов можно настроить с помощью [публикации заготовок](artisan.md#stub-customization). +> **Примечание**\ +> Заготовки тестов можно настроить с помощью [публикации заготовок](artisan.md#stub-customization). После того, как тест был сгенерирован, вы можете определить методы тестирования, как обычно, используя [PHPUnit](https://phpunit.de). Чтобы запустить ваши тесты, выполните команду `vendor/bin/phpunit` или `php artisan test` из вашего терминала: @@ -80,7 +81,8 @@ php artisan make:test UserTest --unit --pest } } -> {note} Если вы определяете свои собственные методы `setUp` / `tearDown` в тестовом классе, обязательно вызывайте соответствующие методы `parent::setUp()` / `parent::tearDown()` родительского класса. +> **Предупреждение**\ +> Если вы определяете свои собственные методы `setUp` / `tearDown` в тестовом классе, обязательно вызывайте соответствующие методы `parent::setUp()` / `parent::tearDown()` родительского класса. ## Запуск тестов @@ -118,7 +120,8 @@ php artisan test --parallel php artisan test --parallel --processes=4 ``` -> {note} При параллельном запуске тестов некоторые параметры PHPUnit (такие как `--do-not-cache-result`) могут быть недоступны. +> **Предупреждение**\ +> При параллельном запуске тестов некоторые параметры PHPUnit (такие как `--do-not-cache-result`) могут быть недоступны. #### Параллельное тестирование и базы данных @@ -188,7 +191,8 @@ php artisan test --parallel --recreate-databases ### Отчет о покрытии тестами -> {note} Данный функционал требует [Xdebug](https://xdebug.org) или [PCOV](https://pecl.php.net/package/pcov). +> **Предупреждение**\ +> Данный функционал требует [Xdebug](https://xdebug.org) или [PCOV](https://pecl.php.net/package/pcov). При выполнении тестов приложения вы можете определить, действительно ли ваши тесты охватывают код приложения и сколько кода приложения используется при выполнении ваших тестов. Для этого вы можете указать флаг `--coverage` при вызове команды `test`: diff --git a/docs/upgrade.md b/docs/upgrade.md index 3937ad3..9de85f8 100644 --- a/docs/upgrade.md +++ b/docs/upgrade.md @@ -37,7 +37,8 @@ #### Приблизительное время обновления: 30 минут -> {tip} Мы стараемся задокументировать все возможные критические изменения. Поскольку некоторые из этих критических изменений находятся в малоизвестных частях фреймворка, только часть этих изменений может повлиять на ваше приложение. Хотите сэкономить время? Вы можете использовать [Laravel Shift](https://laravelshift.com/), чтобы автоматизировать обновления приложений. +> **Примечание**\ +> Мы стараемся задокументировать все возможные критические изменения. Поскольку некоторые из этих критических изменений находятся в малоизвестных частях фреймворка, только часть этих изменений может повлиять на ваше приложение. Хотите сэкономить время? Вы можете использовать [Laravel Shift](https://laravelshift.com/), чтобы автоматизировать обновления приложений. ### Обновление зависимостей @@ -59,7 +60,7 @@ Laravel теперь требует PHP 8.0.2 или выше. -Кроме того, замените `facade/ignition` на `"spatie/laravel-ignition": "^1.0"` в файле `composer.json` вашего приложения. +Кроме того, замените `facade/ignition` на `"spatie/laravel-ignition": "^1.0"` и `pusher/pusher-php-server` (если это применимо) с `"pusher/pusher-php-server": "^5.0"` в файле `composer.json` вашего приложения. Кроме того, следующие пакеты получили новые релизы для поддержки Laravel 9.x. Если применимо, то вы должны прочитать их отдельные руководства перед обновлением: @@ -128,6 +129,14 @@ PHP начинает переходить к требованию определ public function ignore(string $class); ``` +#### Привязка контракта обработчика исключений + +**Вероятность воздействия: очень низкая** + +Ранее, чтобы переопределить обработчик исключений Laravel по умолчанию, пользовательские реализации были привязаны к контейнеру службы с использованием типа `\App\Exceptions\Handler::class`. Однако теперь вы должны привязывать пользовательские реализации, используя тип `\Illuminate\Contracts\Debug\ExceptionHandler::class`. + +Previously, in order to override the default Laravel exception handler, custom implementations were bound into the service container using the `\App\Exceptions\Handler::class` type. However, you should now bind custom implementations using the `\Illuminate\Contracts\Debug\ExceptionHandler::class` type. + ### Шаблонизатор Blade #### Отложенные коллекции и переменная `$loop` @@ -508,7 +517,8 @@ composer require symfony/postmark-mailer symfony/http-client ); }); -> {note} Пожалуйста, внимательно изучите [документацию Symfony Mailer](https://symfony.com/doc/6.0/mailer.html#creating-sending-messages) для взаимодействий с объектом `Symfony\Component\Mime\Email`. +> **Предупреждение**\ +> Пожалуйста, внимательно изучите [документацию Symfony Mailer](https://symfony.com/doc/6.0/mailer.html#creating-sending-messages) для взаимодействий с объектом `Symfony\Component\Mime\Email`. Список ниже содержит более подробный обзор переименованных методов. Многие из этих методов являются низкоуровневыми методами, используемыми для прямого взаимодействия со SwiftMailer / Symfony Mailer, поэтому могут не использоваться в большинстве приложений Laravel: @@ -584,7 +594,8 @@ SwiftMailer предлагал возможность определить со Чтобы узнать больше о доступных параметрах конфигурации, ознакомьтесь с [документацией Symfony Mailer](https://symfony.com/doc/6.0/mailer.html#transport-setup). -> {note} Несмотря на приведенный выше пример, обычно не рекомендуется отключать проверку SSL, поскольку это создает возможность атак типа [«man-in-the-middle»](https://ru.wikipedia.org/wiki/Атака_посредника). +> **Предупреждение**\ +> Несмотря на приведенный выше пример, обычно не рекомендуется отключать проверку SSL, поскольку это создает возможность атак типа [«man-in-the-middle»](https://ru.wikipedia.org/wiki/Атака_посредника). #### Режим аутентификации SMTP diff --git a/docs/valet.md b/docs/valet.md index b7c6207..3c11e13 100644 --- a/docs/valet.md +++ b/docs/valet.md @@ -42,7 +42,7 @@ Out of the box, Valet support includes, but is not limited to: - [Laravel](https://laravel.com) - [Bedrock](https://roots.io/bedrock/) - [CakePHP 3](https://cakephp.org) -- [Concrete5](https://www.concrete5.org/) +- [ConcreteCMS](https://www.concretecms.com/) - [Contao](https://contao.org/en/) - [Craft](https://craftcms.com) - [Drupal](https://www.drupal.org/) @@ -68,7 +68,8 @@ However, you may extend Valet with your own [custom drivers](#custom-valet-drive ## Installation -> {note} Valet requires macOS and [Homebrew](https://brew.sh/). Before installation, you should make sure that no other programs such as Apache or Nginx are binding to your local machine's port 80. +> **Warning** +> Valet requires macOS and [Homebrew](https://brew.sh/). Before installation, you should make sure that no other programs such as Apache or Nginx are binding to your local machine's port 80. To get started, you first need to ensure that Homebrew is up to date using the `update` command: @@ -117,7 +118,8 @@ php@7.2 Once this file has been created, you may simply execute the `valet use` command and the command will determine the site's preferred PHP version by reading the file. -> {note} Valet only serves one PHP version at a time, even if you have multiple PHP versions installed. +> **Warning** +> Valet only serves one PHP version at a time, even if you have multiple PHP versions installed. #### Database @@ -127,12 +129,12 @@ If your application needs a database, check out [DBngin](https://dbngin.com). DB #### Resetting Your Installation -If you are having trouble getting your Valet installation to run properly, executing the `composer global update` command followed by `valet install` will reset your installation and can solve a variety of problems. In rare cases, it may be necessary to "hard reset" Valet by executing `valet uninstall --force` followed by `valet install`. +If you are having trouble getting your Valet installation to run properly, executing the `composer global require laravel/valet` command followed by `valet install` will reset your installation and can solve a variety of problems. In rare cases, it may be necessary to "hard reset" Valet by executing `valet uninstall --force` followed by `valet install`. ### Upgrading Valet -You may update your Valet installation by executing the `composer global update` command in your terminal. After upgrading, it is good practice to run the `valet install` command so Valet can make additional upgrades to your configuration files if necessary. +You may update your Valet installation by executing the `composer global require laravel/valet` command in your terminal. After upgrading, it is good practice to run the `valet install` command so Valet can make additional upgrades to your configuration files if necessary. ## Serving Sites @@ -270,7 +272,8 @@ valet share To stop sharing your site, you may press `Control + C`. Sharing your site using Ngrok requires you to [create an Ngrok account](https://dashboard.ngrok.com/signup) and [setup an authentication token](https://dashboard.ngrok.com/get-started/your-authtoken). -> {tip} You may pass additional Ngrok parameters to the share command, such as `valet share --region=eu`. For more information, consult the [ngrok documentation](https://ngrok.com/docs). +> **Note** +> You may pass additional Ngrok parameters to the share command, such as `valet share --region=eu`. For more information, consult the [ngrok documentation](https://ngrok.com/docs). ### Sharing Sites Via Expose @@ -395,7 +398,8 @@ The `isStaticFile` should determine if the incoming request is for a file that i return false; } -> {note} The `isStaticFile` method will only be called if the `serves` method returns `true` for the incoming request and the request URI is not `/`. +> **Warning** +> The `isStaticFile` method will only be called if the `serves` method returns `true` for the incoming request and the request URI is not `/`. #### The `frontControllerPath` Method @@ -420,6 +424,8 @@ The `frontControllerPath` method should return the fully qualified path to your If you would like to define a custom Valet driver for a single application, create a `LocalValetDriver.php` file in the application's root directory. Your custom driver may extend the base `ValetDriver` class or extend an existing application specific driver such as the `LaravelValetDriver`: + use Valet\Drivers\LaravelValetDriver; + class LocalValetDriver extends LaravelValetDriver { /** @@ -454,6 +460,7 @@ If you would like to define a custom Valet driver for a single application, crea Command | Description ------------- | ------------- +`valet list` | Display a list of all Valet commands. `valet forget` | Run this command from a "parked" directory to remove it from the parked directory list. `valet log` | View a list of logs which are written by Valet's services. `valet paths` | View all of your "parked" paths. @@ -533,4 +540,4 @@ This file is the default Nginx configuration used for building SSL certificates Since macOS 10.14, [access to some files and directories is restricted by default](https://manuals.info.apple.com/MANUALS/1000/MA1902/en_US/apple-platform-security-guide.pdf). These restrictions include the Desktop, Documents, and Downloads directories. In addition, network volume and removable volume access is restricted. Therefore, Valet recommends your site folders are located outside of these protected locations. -However, if you wish to serve sites from within one of those locations, you will need to give Nginx "Full Disk Access". Otherwise, you may encounter server errors or other unpredictable behavior from Nginx, especially when serving static assets. Typically, macOS will automatically prompt you to grant Nginx full access to these locations. Or, you may do so manually via `System Preferences` > `Security & Privacy` > `Privacy` and selecting `Full Disk Access`. Next, enable any `nginx` entries in the main window pane. +However, if you wish to serve sites from within one of those locations, you will need to give Nginx "Full Disk Access". Otherwise, you may encounter server errors or other unpredictable behavior from Nginx, especially when serving static assets. Typically, macOS will automatically prompt you to grant Nginx full access to these locations. Or, you may do so manually via `System Preferences` > `Security & Privacy` > `Privacy` and selecting `Full Disk Access`. Next, enable any `nginx` entries in the main window pane. \ No newline at end of file diff --git a/docs/validation.md b/docs/validation.md index c9a13ea..45fac0e 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -316,7 +316,8 @@ php artisan make:request StorePostRequest ]; } -> {tip} Вы можете объявить любые зависимости, которые вам нужны, в сигнатуре метода `rules`. Они будут автоматически извлечены через [контейнер служб](container.md) Laravel. +> **Примечание**\ +> Вы можете объявить любые зависимости, которые вам нужны, в сигнатуре метода `rules`. Они будут автоматически извлечены через [контейнер служб](container.md) Laravel. Итак, как анализируются правила валидации? Все, что вам нужно сделать, это объявить зависимость от запроса в методе вашего контроллера. Входящий запрос формы проверяется до вызова метода контроллера, что означает, что вам не нужно загромождать контроллер какой-либо логикой валидации: @@ -435,7 +436,8 @@ php artisan make:request StorePostRequest return true; } -> {tip} Вы можете объявить любые зависимости, которые вам нужны, в сигнатуре метода `authorize`. Они будут автоматически извлечены через [контейнер служб](container.md) Laravel. +> **Примечание**\ +> Вы можете объявить любые зависимости, которые вам нужны, в сигнатуре метода `authorize`. Они будут автоматически извлечены через [контейнер служб](container.md) Laravel. ### Корректировка сообщений об ошибках @@ -1091,7 +1093,8 @@ The credit card number field is required when payment type is credit card. Валидатор `filter`, который использует функцию `filter_var` PHP, поставляется с Laravel и применялся по умолчанию до Laravel версии 5.8. -> {note} Валидаторы `dns` и `spoof` требуют расширения `intl` PHP. +> **Предупреждение**\ +> Валидаторы `dns` и `spoof` требуют расширения `intl` PHP. #### ends_with:_foo_,_bar_,... @@ -1110,7 +1113,8 @@ The credit card number field is required when payment type is credit card. 'status' => [new Enum(ServerStatus::class)], ]); -> {note} Перечисляемые типы доступны только в [PHP 8.1+](https://www.php.net/manual/ru/language.enumerations.php). +> **Предупреждение**\ +> Перечисляемые типы доступны только в [PHP 8.1+](https://www.php.net/manual/ru/language.enumerations.php). #### exclude @@ -1262,7 +1266,8 @@ The credit card number field is required when payment type is credit card. Проверяемое поле должно быть целым числом. -> {note} Это правило валидации не проверяет, что значение поля относится к типу переменной `integer`, а только что значение поля относится к типу, принятому правилом `FILTER_VALIDATE_INT` PHP. Если вам нужно проверить значение поля в качестве числа, используйте это правило в сочетании с [правилом валидации `numeric`](#rule-numeric). +> **Предупреждение**\ +> Это правило валидации не проверяет, что значение поля относится к типу переменной `integer`, а только что значение поля относится к типу, принятому правилом `FILTER_VALIDATE_INT` PHP. Если вам нужно проверить значение поля в качестве числа, используйте это правило в сочетании с [правилом валидации `numeric`](#rule-numeric). #### ip @@ -1337,7 +1342,8 @@ The credit card number field is required when payment type is credit card. Проверяемое поле должно быть кратным _value_. -> {note} Для использования правила `multiple_of` требуется [расширение `bcmath` PHP](https://www.php.net/manual/ru/book.bc.php). +> **Предупреждение**\ +> Для использования правила `multiple_of` требуется [расширение `bcmath` PHP](https://www.php.net/manual/ru/book.bc.php). #### not_in:_foo_,_bar_,... @@ -1360,7 +1366,8 @@ The credit card number field is required when payment type is credit card. Внутренне это правило использует функцию `preg_match` PHP. Указанный шаблон должен подчиняться тому же форматированию, требуемому `preg_match`, и, следовательно, также включать допустимые разделители. Например: `'email' => 'not_regex:/^.+$/i'`. -> {note} При использовании шаблонов `regex` / `not_regex` может потребоваться указать ваши правила валидации с использованием массива вместо использования разделителей `|`, особенно если регулярное выражение содержит символ `|`. +> **Предупреждение**\ +> При использовании шаблонов `regex` / `not_regex` может потребоваться указать ваши правила валидации с использованием массива вместо использования разделителей `|`, особенно если регулярное выражение содержит символ `|`. #### nullable @@ -1377,7 +1384,8 @@ The credit card number field is required when payment type is credit card. Проверяемое поле должно соответствовать паролю аутентифицированного пользователя. -> {note} Это правило было переименовано в `current_password` с его дальнейшим удалением в Laravel 9. Вместо этого используйте правило [Current Password](#rule-current-password). +> **Предупреждение**\ +> Это правило было переименовано в `current_password` с его дальнейшим удалением в Laravel 9. Вместо этого используйте правило [Current Password](#rule-current-password). #### present @@ -1424,7 +1432,8 @@ The credit card number field is required when payment type is credit card. Внутренне это правило использует функцию `preg_match` PHP. Указанный шаблон должен подчиняться тому же форматированию, требуемому `preg_match`, и, следовательно, также включать допустимые разделители. Например: `'email' => 'regex:/^.+@.+$/i'`. -> {note} При использовании шаблонов `regex` / `not_regex` может потребоваться указать ваши правила валидации с использованием массива вместо использования разделителей `|`, особенно если регулярное выражение содержит символ `|`. +> **Предупреждение**\ +> При использовании шаблонов `regex` / `not_regex` может потребоваться указать ваши правила валидации с использованием массива вместо использования разделителей `|`, особенно если регулярное выражение содержит символ `|`. #### required @@ -1562,7 +1571,8 @@ The credit card number field is required when payment type is credit card. ], ]); -> {note} Вы никогда не должны передавать какое-либо введенное пользователем значение из запроса в метод `ignore`. Вместо этого вы должны передавать только сгенерированный системой уникальный идентификатор, такой как автоинкрементный идентификатор или UUID экземпляра модели Eloquent. В противном случае ваше приложение будет уязвимо для атаки с использованием SQL-инъекции. +> **Предупреждение**\ +> Вы никогда не должны передавать какое-либо введенное пользователем значение из запроса в метод `ignore`. Вместо этого вы должны передавать только сгенерированный системой уникальный идентификатор, такой как автоинкрементный идентификатор или UUID экземпляра модели Eloquent. В противном случае ваше приложение будет уязвимо для атаки с использованием SQL-инъекции. Вместо того, чтобы передавать значение ключа модели методу `ignore`, вы также можете передать весь экземпляр модели. Laravel автоматически извлечет ключ из модели: @@ -1627,7 +1637,8 @@ The credit card number field is required when payment type is credit card. В приведенном выше примере поле `email` будет проверено, только если оно присутствует в массиве `$request->all()`. -> {tip} Если вы пытаетесь проверить поле, которое всегда должно присутствовать, но может быть пустым, ознакомьтесь с [этим примечанием о необязательных полях](#a-note-on-optional-fields). +> **Примечание**\ +> Если вы пытаетесь проверить поле, которое всегда должно присутствовать, но может быть пустым, ознакомьтесь с [этим примечанием о необязательных полях](#a-note-on-optional-fields). #### Комплексная условная проверка @@ -1653,7 +1664,8 @@ The credit card number field is required when payment type is credit card. return $input->games >= 100; }); -> {tip} Параметр `$input`, переданный вашему замыканию, будет экземпляром `Illuminate\Support\Fluent` и может использоваться при валидации для доступа к вашим входящим данным и файлам запроса. +> **Примечание**\ +> Параметр `$input`, переданный вашему замыканию, будет экземпляром `Illuminate\Support\Fluent` и может использоваться при валидации для доступа к вашим входящим данным и файлам запроса. #### Комплексная условная валидация массива @@ -2039,4 +2051,5 @@ php artisan make:rule Uppercase php artisan make:rule Uppercase --implicit ``` -> {note} «Неявное» правило только _подразумевает_, что атрибут является обязательным к валидации. В действительности, решать только вам, будет ли пустой или отсутствующий атрибут считаться невалидным. +> **Предупреждение**\ +> «Неявное» правило только _подразумевает_, что атрибут является обязательным к валидации. В действительности, решать только вам, будет ли пустой или отсутствующий атрибут считаться невалидным. diff --git a/docs/verification.md b/docs/verification.md index 1713349..ff15379 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -16,7 +16,8 @@ Многие веб-приложения требуют от пользователей подтверждения своего адреса электронной почты перед использованием приложения. Вместо того, чтобы заставлять вас самостоятельно реализовывать этот функционал повторно для каждого создаваемого вами приложения, Laravel предлагает удобные встроенные службы для отправки и проверки запросов подтверждения адреса электронной почты. -> {tip} Хотите быстро начать? Установите один из [стартовых комплектов](starter-kits.md) в новое приложение Laravel. Стартовые комплекты позаботятся о построении всей вашей системы аутентификации, включая поддержку подтверждения электронной почты. +> **Примечание**\ +> Хотите быстро начать? Установите один из [стартовых комплектов](starter-kits.md) в новое приложение Laravel. Стартовые комплекты позаботятся о построении всей вашей системы аутентификации, включая поддержку подтверждения электронной почты. ### Подготовка модели @@ -75,7 +76,8 @@ php artisan migrate Маршрут, который возвращает уведомление о подтверждении по электронной почте, должен называться `verification.notice`. Важно, чтобы маршруту было присвоено это точное имя, поскольку посредник `verify`, [включенный в Laravel](#protecting-routes), будет автоматически перенаправлять на это имя маршрута, если пользователь не подтвердил свой адрес электронной почты. -> {tip} При выполнении проверки электронной почты самостоятельно, вам необходимо определить содержание страницы уведомления о проверке. Если вам необходим каркас, включающий все необходимые страницы для аутентификации и проверки, ознакомьтесь со [стартовыми комплектами приложений Laravel](starter-kits.md). +> **Примечание**\ +> При выполнении проверки электронной почты самостоятельно, вам необходимо определить содержание страницы уведомления о проверке. Если вам необходим каркас, включающий все необходимые страницы для аутентификации и проверки, ознакомьтесь со [стартовыми комплектами приложений Laravel](starter-kits.md). ### Обработчик проверки электронной почты @@ -148,7 +150,8 @@ php artisan migrate }); } -> {tip} Чтобы узнать больше о почтовых уведомлениях, обратитесь к [документации по почтовым уведомлениям](notifications.md#mail-notifications). +> **Примечание**\ +> Чтобы узнать больше о почтовых уведомлениях, обратитесь к [документации по почтовым уведомлениям](notifications.md#mail-notifications). ## События diff --git a/docs/views.md b/docs/views.md index 0004f06..41ae2d9 100644 --- a/docs/views.md +++ b/docs/views.md @@ -32,7 +32,8 @@ return view('greeting', ['name' => 'James']); }); -> {tip} Ищете дополнительную информацию о том, как писать шаблоны Blade? Ознакомьтесь с полной [документацией по Blade](blade.md), чтобы начать работу. +> **Примечание**\ +> Ищете дополнительную информацию о том, как писать шаблоны Blade? Ознакомьтесь с полной [документацией по Blade](blade.md), чтобы начать работу. ## Создание и отрисовка шаблонов @@ -60,7 +61,8 @@ return view('admin.profile', $data); -> {note} Имена каталогов шаблонов не должны содержать символа `.`. +> **Предупреждение**\ +> Имена каталогов шаблонов не должны содержать символа `.`. ### Использование первого доступного шаблона @@ -177,7 +179,8 @@ } } -> {note} Помните, что если вы создаете нового поставщика служб, который будет содержать регистрации вашего компоновщика, то вам нужно будет добавить поставщика служб в массив `providers` в конфигурационном файле `config/app.php`. +> **Предупреждение**\ +> Помните, что если вы создаете нового поставщика служб, который будет содержать регистрации вашего компоновщика, то вам нужно будет добавить поставщика служб в массив `providers` в конфигурационном файле `config/app.php`. Теперь, когда мы зарегистрировали компоновщик, метод `compose` класса `App\View\Composers\ProfileComposer` будет выполняться каждый раз, когда отрисовывается шаблон профиля. Давайте посмотрим на пример класса компоновщика: diff --git a/docs/vite.md b/docs/vite.md new file mode 100644 index 0000000..e2e94f2 --- /dev/null +++ b/docs/vite.md @@ -0,0 +1,765 @@ +# Laravel 9 · Asset Bundling (Vite) + +- [Introduction](#introduction) +- [Installation & Setup](#installation) + - [Installing Node](#installing-node) + - [Installing Vite And The Laravel Plugin](#installing-vite-and-laravel-plugin) + - [Configuring Vite](#configuring-vite) + - [Loading Your Scripts And Styles](#loading-your-scripts-and-styles) +- [Running Vite](#running-vite) +- [Working With JavaScript](#working-with-scripts) + - [Aliases](#aliases) + - [Vue](#vue) + - [React](#react) + - [Inertia](#inertia) + - [URL Processing](#url-processing) +- [Working With Stylesheets](#working-with-stylesheets) +- [Working With Blade & Routes](#working-with-blade-and-routes) + - [Processing Static Assets With Vite](#blade-processing-static-assets) + - [Refreshing On Save](#blade-refreshing-on-save) + - [Aliases](#blade-aliases) +- [Custom Base URLs](#custom-base-urls) +- [Environment Variables](#environment-variables) +- [Disabling Vite In Tests](#disabling-vite-in-tests) +- [Server-Side Rendering (SSR)](#ssr) +- [Script & Style Tag Attributes](#script-and-style-attributes) + - [Content Security Policy (CSP) Nonce](#content-security-policy-csp-nonce) + - [Subresource Integrity (SRI)](#subresource-integrity-sri) + - [Arbitrary Attributes](#arbitrary-attributes) +- [Advanced Customization](#advanced-customization) + + +## Introduction + +[Vite](https://vitejs.dev) is a modern frontend build tool that provides an extremely fast development environment and bundles your code for production. When building applications with Laravel, you will typically use Vite to bundle your application's CSS and JavaScript files into production ready assets. + +Laravel integrates seamlessly with Vite by providing an official plugin and Blade directive to load your assets for development and production. + +> **Note** +> Are you running Laravel Mix? Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). + + +#### Choosing Between Vite And Laravel Mix + +Before transitioning to Vite, new Laravel applications utilized [Mix](https://laravel-mix.com/), which is powered by [webpack](https://webpack.js.org/), when bundling assets. Vite focuses on providing a faster and more productive experience when building rich JavaScript applications. If you are developing a Single Page Application (SPA), including those developed with tools like [Inertia](https://inertiajs.com), Vite will be the perfect fit. + +Vite also works well with traditional server-side rendered applications with JavaScript "sprinkles", including those using [Livewire](https://laravel-livewire.com). However, it lacks some features that Laravel Mix supports, such as the ability to copy arbitrary assets into the build that are not referenced directly in your JavaScript application. + + +#### Migrating Back To Mix + +Have you started a new Laravel application using our Vite scaffolding but need to move back to Laravel Mix and webpack? No problem. Please consult our [official guide on migrating from Vite to Mix](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-vite-to-laravel-mix). + + +## Installation & Setup + +> **Note** +> The following documentation discusses how to manually install and configure the Laravel Vite plugin. However, Laravel's [starter kits](/docs/{{version}}/starter-kits) already include all of this scaffolding and are the fastest way to get started with Laravel and Vite. + + +### Installing Node + +You must ensure that Node.js (16+) and NPM are installed before running Vite and the Laravel plugin: + +```sh +node -v +npm -v +``` + +You can easily install the latest version of Node and NPM using simple graphical installers from [the official Node website](https://nodejs.org/en/download/). Or, if you are using [Laravel Sail](https://laravel.com/docs/{{version}}/sail), you may invoke Node and NPM through Sail: + +```sh +./vendor/bin/sail node -v +./vendor/bin/sail npm -v +``` + + +### Installing Vite And The Laravel Plugin + +Within a fresh installation of Laravel, you will find a `package.json` file in the root of your application's directory structure. The default `package.json` file already includes everything you need to get started using Vite and the Laravel plugin. You may install your application's frontend dependencies via NPM: + +```sh +npm install +``` + + +### Configuring Vite + +Vite is configured via a `vite.config.js` file in the root of your project. You are free to customize this file based on your needs, and you may also install any other plugins your application requires, such as `@vitejs/plugin-vue` or `@vitejs/plugin-react`. + +The Laravel Vite plugin requires you to specify the entry points for your application. These may be JavaScript or CSS files, and include preprocessed languages such as TypeScript, JSX, TSX, and Sass. + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel([ + 'resources/css/app.css', + 'resources/js/app.js', + ]), + ], +}); +``` + +If you are building an SPA, including applications built using Inertia, Vite works best without CSS entry points: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel([ + 'resources/css/app.css', // [tl! remove] + 'resources/js/app.js', + ]), + ], +}); +``` + +Instead, you should import your CSS via JavaScript. Typically, this would be done in your application's `resources/js/app.js` file: + +```js +import './bootstrap'; +import '../css/app.css'; // [tl! add] +``` + +The Laravel plugin also supports multiple entry points and advanced configuration options such as [SSR entry points](#ssr). + + +#### Working With A Secure Development Server + +If your local development web server is serving your application via HTTPS, you may run into issues connecting to the Vite development server. + +If you are using [Laravel Valet](/docs/{{version}}/valet) for local development and have run the [secure command](/docs/{{version}}/valet#securing-sites) against your application, you may configure the Vite development server to automatically use Valet's generated TLS certificates: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + valetTls: 'my-app.test', // [tl! add] + }), + ], +}); +``` + +When using another web server, you should generate a trusted certificate and manually configure Vite to use the generated certificates: + +```js +// ... +import fs from 'fs'; // [tl! add] + +const host = 'my-app.test'; // [tl! add] + +export default defineConfig({ + // ... + server: { // [tl! add] + host, // [tl! add] + hmr: { host }, // [tl! add] + https: { // [tl! add] + key: fs.readFileSync(`/path/to/${host}.key`), // [tl! add] + cert: fs.readFileSync(`/path/to/${host}.crt`), // [tl! add] + }, // [tl! add] + }, // [tl! add] +}); +``` + +If you are unable to generate a trusted certificate for your system, you may install and configure the [`@vitejs/plugin-basic-ssl` plugin](https://github.com/vitejs/vite-plugin-basic-ssl). When using untrusted certificates, you will need to accept the certificate warning for Vite's development server in your browser by following the "Local" link in your console when running the `npm run dev` command. + + +### Loading Your Scripts And Styles + +With your Vite entry points configured, you only need reference them in a `@vite()` Blade directive that you add to the `` of your application's root template: + +```blade + + + {{-- ... --}} + + @vite(['resources/css/app.css', 'resources/js/app.js']) + +``` + +If you're importing your CSS via JavaScript, you only need to include the JavaScript entry point: + +```blade + + + {{-- ... --}} + + @vite('resources/js/app.js') + +``` + +The `@vite` directive will automatically detect the Vite development server and inject the Vite client to enable Hot Module Replacement. In build mode, the directive will load your compiled and versioned assets, including any imported CSS. + +If needed, you may also specify the build path of your compiled assets when invoking the `@vite` directive: + +```blade + + + {{-- Given build path is relative to public path. --}} + + @vite('resources/js/app.js', 'vendor/courier/build') + +``` + + +## Running Vite + +There are two ways you can run Vite. You may run the development server via the `dev` command, which is useful while developing locally. The development server will automatically detect changes to your files and instantly reflect them in any open browser windows. + +Or, running the `build` command will version and bundle your application's assets and get them ready for you to deploy to production: + +```shell +# Run the Vite development server... +npm run dev + +# Build and version the assets for production... +npm run build +``` + + +## Working With JavaScript + + +### Aliases + +By default, The Laravel plugin provides a common alias to help you hit the ground running and conveniently import your application's assets: + +```js +{ + '@' => '/resources/js' +} +``` + +You may overwrite the `'@'` alias by adding your own to the `vite.config.js` configuration file: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel(['resources/ts/app.tsx']), + ], + resolve: { + alias: { + '@': '/resources/ts', + }, + }, +}); +``` + + +### Vue + +There are a few additional options you will need to include in the `vite.config.js` configuration file when using the Vue plugin with the Laravel plugin: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import vue from '@vitejs/plugin-vue'; + +export default defineConfig({ + plugins: [ + laravel(['resources/js/app.js']), + vue({ + template: { + transformAssetUrls: { + // The Vue plugin will re-write asset URLs, when referenced + // in Single File Components, to point to the Laravel web + // server. Setting this to `null` allows the Laravel plugin + // to instead re-write asset URLs to point to the Vite + // server instead. + base: null, + + // The Vue plugin will parse absolute URLs and treat them + // as absolute paths to files on disk. Setting this to + // `false` will leave absolute URLs un-touched so they can + // reference assets in the public directory as expected. + includeAbsolute: false, + }, + }, + }), + ], +}); +``` + +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Vue, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Vue, and Vite. + + +### React + +When using Vite with React, you will need to ensure that any files containing JSX have a `.jsx` or `.tsx` extension, remembering to update your entry point, if required, as [shown above](#configuring-vite). You will also need to include the additional `@viteReactRefresh` Blade directive alongside your existing `@vite` directive. + +```blade +@viteReactRefresh +@vite('resources/js/app.jsx') +``` + +The `@viteReactRefresh` directive must be called before the `@vite` directive. + +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, React, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, React, and Vite. + + +### Inertia + +The Laravel Vite plugin provides a convenient `resolvePageComponent` function to help you resolve your Inertia page components. Below is an example of the helper in use with Vue 3; however, you may also utilize the function in other frameworks such as React: + +```js +import { createApp, h } from 'vue'; +import { createInertiaApp } from '@inertiajs/inertia-vue3'; +import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; + +createInertiaApp({ + resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')), + setup({ el, App, props, plugin }) { + createApp({ render: () => h(App, props) }) + .use(plugin) + .mount(el) + }, +}); +``` + +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia, and Vite. + + +### URL Processing + +When using Vite and referencing assets in your application's HTML, CSS, or JS, there are a couple of caveats to consider. First, if you reference assets with an absolute path, Vite will not include the asset in the build; therefore, you should ensure that the asset is available in your public directory. + +When referencing relative asset paths, you should remember that the paths are relative to the file where they are referenced. Any assets referenced via a relative path will be re-written, versioned, and bundled by Vite. + +Consider the following project structure: + +```nothing +public/ + taylor.png +resources/ + js/ + Pages/ + Welcome.vue + images/ + abigail.png +``` + +The following example demonstrates how Vite will treat relative and absolute URLs: + +```html + + + + + +``` + + +## Working With Stylesheets + +You can learn more about Vite's CSS support within the [Vite documentation](https://vitejs.dev/guide/features.html#css). If you are using PostCSS plugins such as [Tailwind](https://tailwindcss.com), you may create a `postcss.config.js` file in the root of your project and Vite will automatically apply it: + +```js +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; +``` + + +## Working With Blade & Routes + + +### Processing Static Assets With Vite + +When referencing assets in your JavaScript or CSS, Vite automatically processes and versions them. In addition, when building Blade based applications, Vite can also process and version static assets that you reference solely in Blade templates. + +However, in order to accomplish this, you need to make Vite aware of your assets by importing the static assets into the application's entry point. For example, if you want to process and version all images stored in `resources/images` and all fonts stored in `resources/fonts`, you should add the following in your application's `resources/js/app.js` entry point: + +```js +import.meta.glob([ + '../images/**', + '../fonts/**', +]); +``` + +These assets will now be processed by Vite when running `npm run build`. You can then reference these assets in Blade templates using the `Vite::asset` method, which will return the versioned URL for a given asset: + +```blade + +``` + + +### Refreshing On Save + +When your application is built using traditional server-side rendering with Blade, Vite can improve your development workflow by automatically refreshing the browser when you make changes to view files in your application. To get started, you can simply specify the `refresh` option as `true`. + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + refresh: true, + }), + ], +}); +``` + +When the `refresh` option is `true`, saving files in the following directories will trigger the browser to perform a full page refresh while you are running `npm run dev`: + +- `app/View/Components/**` +- `lang/**` +- `resources/lang/**` +- `resources/views/**` +- `routes/**` + +Watching the `routes/**` directory is useful if you are utilizing [Ziggy](https://github.com/tighten/ziggy) to generate route links within your application's frontend. + +If these default paths do not suit your needs, you can specify your own list of paths to watch: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + refresh: ['resources/views/**'], + }), + ], +}); +``` + +Under the hood, the Laravel Vite plugin uses the [`vite-plugin-full-reload`](https://github.com/ElMassimo/vite-plugin-full-reload) package, which offers some advanced configuration options to fine-tune this feature's behavior. If you need this level of customization, you may provide a `config` definition: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + refresh: [{ + paths: ['path/to/watch/**'], + config: { delay: 300 } + }], + }), + ], +}); +``` + + +### Aliases + +It is common in JavaScript applications to [create aliases](#aliases) to regularly referenced directories. But, you may also create aliases to use in Blade by using the `macro` method on the `Illuminate\Support\Vite` class. Typically, "macros" should be defined within the `boot` method of a [service provider](/docs/{{version}}/providers): + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + Vite::macro('image', fn ($asset) => $this->asset("resources/images/{$asset}")); + } + +Once a macro has been defined, it can be invoked within your templates. For example, we can use the `image` macro defined above to reference an asset located at `resources/images/logo.png`: + +```blade +Laravel Logo +``` + + +## Custom Base URLs + +If your Vite compiled assets are deployed to a domain separate from your application, such as via a CDN, you must specify the `ASSET_URL` environment variable within your application's `.env` file: + +```env +ASSET_URL=https://cdn.example.com +``` + +After configuring the asset URL, all re-written URLs to your assets will be prefixed with the configured value: + +```nothing +https://cdn.example.com/build/assets/app.9dce8d17.js +``` + +Remember that [absolute URLs are not re-written by Vite](#url-processing), so they will not be prefixed. + + +## Environment Variables + +You may inject environment variables into your JavaScript by prefixing them with `VITE_` in your application's `.env` file: + +```env +VITE_SENTRY_DSN_PUBLIC=http://example.com +``` + +You may access injected environment variables via the `import.meta.env` object: + +```js +import.meta.env.VITE_SENTRY_DSN_PUBLIC +``` + + +## Disabling Vite In Tests + +Laravel's Vite integration will attempt to resolve your assets while running your tests, which requires you to either run the Vite development server or build your assets. + +If you would prefer to mock Vite during testing, you may call the `withoutVite` method, which is is available for any tests that extend Laravel's `TestCase` class: + +```php +use Tests\TestCase; + +class ExampleTest extends TestCase +{ + public function test_without_vite_example() + { + $this->withoutVite(); + + // ... + } +} +``` + +If you would like to disable Vite for all tests, you may call the `withoutVite` method from the `setUp` method on your base `TestCase` class: + +```php +withoutVite(); + }// [tl! add:end] +} +``` + + +## Server-Side Rendering (SSR) + +The Laravel Vite plugin makes it painless to set up server-side rendering with Vite. To get started, create an SSR entry point at `resources/js/ssr.js` and specify the entry point by passing a configuration option to the Laravel plugin: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: 'resources/js/app.js', + ssr: 'resources/js/ssr.js', + }), + ], +}); +``` + +To ensure you don't forget to rebuild the SSR entry point, we recommend augmenting the "build" script in your application's `package.json` to create your SSR build: + +```json +"scripts": { + "dev": "vite", + "build": "vite build" // [tl! remove] + "build": "vite build && vite build --ssr" // [tl! add] +} +``` + +Then, to build and start the SSR server, you may run the following commands: + +```sh +npm run build +node bootstrap/ssr/ssr.mjs +``` + +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. + + +## Script & Style Tag Attributes + + +### Content Security Policy (CSP) Nonce + +If you wish to include a [`nonce` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) on your script and style tags as part of your [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), you may generate or specify a nonce using the `useCspNonce` method within a custom [middleware](/docs/{{version}}/middleware): + +```php +withHeaders([ + 'Content-Security-Policy' => "script-src 'nonce-".Vite::cspNonce()."'", + ]); + } +} +``` + +After invoking the `useCspNonce` method, Laravel will automatically include the `nonce` attributes on all generated script and style tags. + +If you need to specify the nonce elsewhere, including the [Ziggy `@route` directive](https://github.com/tighten/ziggy#using-routes-with-a-content-security-policy) included with Laravel's [starter kits](/docs/{{version}}/starter-kits), you may retrieve it using the `cspNonce` method: + +```blade +@routes(nonce: Vite::cspNonce()) +``` + +If you already have a nonce that you would like to instruct Laravel to use, you may pass the nonce to the `useCspNonce` method: + +```php +Vite::useCspNonce($nonce); +``` + + +### Subresource Integrity (SRI) + +If your Vite manifest includes `integrity` hashes for your assets, Laravel will automatically add the `integrity` attribute on any script and style tags it generates in order to enforce [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). By default, Vite does not include the `integrity` hash in its manifest, but you may enable it by installing the [`vite-plugin-manifest-uri`](https://www.npmjs.com/package/vite-plugin-manifest-sri) NPM plugin: + +```shell +npm install -D vite-plugin-manifest-sri +``` + +You may then enable this plugin in your `vite.config.js` file: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import manifestSRI from 'vite-plugin-manifest-sri';// [tl! add] + +export default defineConfig({ + plugins: [ + laravel({ + // ... + }), + manifestSRI(),// [tl! add] + ], +}); +``` + +If required, you may also customize the manifest key where the integrity hash can be found: + +```php +use Illuminate\Support\Facades\Vite; + +Vite::useIntegrityKey('custom-integrity-key'); +``` + +If you would like to disable this auto-detection completely, you may pass `false` to the `useIntegrityKey` method: + +```php +Vite::useIntegrityKey(false); +``` + + +### Arbitrary Attributes + +If you need to include additional attributes on your script and style tags, such as the [`data-turbo-track`](https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change) attribute, you may specify them via the `useScriptTagAttributes` and `useStyleTagAttributes` methods. Typically, this methods should be invoked from a [service provider](/docs/{{version}}/providers): + +```php +use Illuminate\Support\Facades\Vite; + +Vite::useScriptTagAttributes([ + 'data-turbo-track' => 'reload', // Specify a value for the attribute... + 'async' => true, // Specify an attribute without a value... + 'integrity' => false, // Exclude an attribute that would otherwise be included... +]); + +Vite::useStyleTagAttributes([ + 'data-turbo-track' => 'reload', +]); +``` + +If you need to conditionally add attributes, you may pass a callback that will receive the asset source path, its URL, its manifest chunk, and the entire manifest: + +```php +use Illuminate\Support\Facades\Vite; + +Vite::useScriptTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [ + 'data-turbo-track' => $src === 'resources/js/app.js' ? 'reload' : false, +]); + +Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [ + 'data-turbo-track' => $chunk && $chunk['isEntry'] ? 'reload' : false, +]); +``` + +> **Warning** +> The `$chunk` and `$manifest` arguments will be `null` while the Vite development server is running. + + +## Advanced Customization + +Out of the box, Laravel's Vite plugin uses sensible conventions that should work for the majority of applications; however, sometimes you may need to customize Vite's behavior. To enable additional customization options, we offer the following methods and options which can be used in place of the `@vite` Blade directive: + +```blade + + + {{-- ... --}} + + {{ + Vite::useHotFile(storage_path('vite.hot')) // Customize the "hot" file... + ->useBuildDirectory('bundle') // Customize the build directory... + ->useManifestFilename('assets.json') // Customize the manifest filename... + ->withEntryPoints(['resources/js/app.js']) // Specify the entry points... + }} + +``` + +Within the `vite.config.js` file, you should then specify the same configuration: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + hotFile: 'storage/vite.hot', // Customize the "hot" file... + buildDirectory: 'bundle', // Customize the build directory... + input: ['resources/js/app.js'], // Specify the entry points... + }), + ], + build: { + manifest: 'assets.json', // Customize the manifest filename... + }, +}); +```