Download the PHP package lemax10/simple-actions without Composer
On this page you can find all versions of the php package lemax10/simple-actions. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package simple-actions
Simple Actions
Language: English
Пакет для реализации паттерна простых переиспользуемых действий (Actions) в Laravel приложениях. Вдохновлен Laravel Actions, но не перегружен контекстами. Основной упор сосредоточен на принципе 1 объект экшена = 1 действие. Для упрощения реализации логики с участием множества действий, предусмотрены сценарии (UseCases). Сценарии -- это агрегация множества действий в единый сценарий.
Цель пакета привнести удобство для архитектуры кода, решить некоторые рутинные операции и задачи, но при этом оставаться максимально простым без перегруженности контекста, не раздувая и не размывая ответсвенность объекта.
Установка
Artisan команды генерации
Пакет добавляет команды для быстрого создания заготовок:
Что будет создано:
app/Actions/User/CreateUserAction.phpapp/UseCases/User/RegisterUserUseCase.php
Можно указать полное имя с суффиксом, если необходимо:
Флаг --force перезапишет существующий файл.
Основные возможности
- Простой и понятный API для создания Actions
- UseCase паттерн - агрегирование Actions в сценарии
- DIP (SOLID) - загрузка через Service Container, подмена реализаций
- Полный цикл жизни событий (beforeRun, running, ran, failed, afterRun)
- Observer паттерн подобно Eloquent
- Управление транзакциями БД
- Продвинутое кеширование результатов (поддержка штатного Laravel Cache драйвера или любого поддерживаемого им)
- Мемоизация - кеширование в памяти на время запроса
- Idempotency - защита от повторного параллельного выполнения и от возможного эффекта "гонка".
- Условное выполнение
- Хелперы для удобного использования
Быстрый старт
Создание Action
Использование
Idempotency (опционально)
Для защиты от повторного параллельного выполнения одного и того же действия:
Если ключ уже использовался, вернется сохраненный результат без повторного выполнения handle.
Если такой же ключ сейчас "в процессе", будет выброшено исключение ActionIdempotencyInProgressException.
По умолчанию используется cache-based репозиторий. Репозиторий можно переключить:
Можно регистрировать собственные idempotency-драйверы через IdempotencyRepositoryManager::extend(...). К примеру если Вам небходимо хранить блокировки в БД или абстрактном хранилище.
UseCase - Сценарии из Actions
UseCase это полноценный Action, агрегирующий другие Actions:
Типизация результата
Чтобы приложение сохраняло строгую типизацию при работе с run() и хелперами action()/usecase(), укажите возвращаемый тип через PHPDoc-дженерик:
false возможен, если выполнение было остановлено в beforeRun/running событии.
События
Цикл жизни событий
Action имеет полный цикл жизни с 5 событиями:
- beforeRun - перед началом выполнения
- running - непосредственно перед вызовом handle()
- ran - после успешного выполнения
- failed - при ошибке выполнения
- afterRun - всегда выполняется в конце
Регистрация слушателей
Остановка выполнения
События beforeRun и running могут остановить выполнение, вернув false:
Observer паттерн
Создайте observer для группировки логики событий:
Отключение событий
Локальные события для конкретного экземпляра
Если нужно одноразово повесить хук/хуки только на один вызов:
Доступные локальные методы:
before (beforeRun),
runningLocal (running),
then (ran),
onFail (failed),
after (afterRun).
**Возврат false из локальных before/runningLocal останавливает только текущий экземпляр события.
Условные локальные события
Хелперы *When / *Unless запускают локальный колбэк по условию (Boolean или Closure, аргументы — такие же, что и у run):
Unless инвертирует условие, When.
Локальные хуки не влияют на другие экземпляры и не добавляют глобальных слушателей или событий.
Транзакции
Автоматические транзакции
Динамическое управление
Кеширование
Базовое кеширование
Автогенерация ключей
Теги кеша
Выбор драйвера кеша
Условное кеширование
Управление кешем
Мемоизация в памяти
Мемоизация позволяет сохранять результаты выполнения Action в памяти PHP на время текущего запроса. Это помогает избежать повторного выполнения одинаковых действий, запросов к БД или внешним API в рамках одного запроса.
Отличие от кеширования
- Кеширование (
remember()) - сохраняет результат в кеш (CacheManager Laravel) между запросами - Мемоизация (
memo()) - сохраняет результат в памяти PHP только на время текущего запроса
Базовое использование
Принудительное обновление
События и мемоизация
По умолчанию, когда результат берётся из памяти (мемоизирован), события НЕ запускаются повторно. Это связано с производительностью и рализовано в качестве оптимизации.
Если все-таки по какой-то причине необходимо запускать события даже для мемоизированных результатов, используйте аргумент forceEvents:
Небольшие рекомендации:
Когда использовать forceEvents:
- Нужны побочные эффекты в событиях (очистка кеша, уведомления)
- События используются для аудита/логирования каждого обращения
- Отладка - хотите видеть все вызовы
Когда НЕ использовать forceEvents:
- События только для внутренней логики Action
- Производительность критична (overhead дублирование эвентов будет замедлять работу приложения)
- События дублируют то, что уже произошло при первом вызове
Пользовательский ключ мемоизации
По умолчанию ключ генерируется на основе аргументов. Однако можно задать свой ключ:
Управление мемоизацией (для сложных сценариев)
Практические примеры
Избежать N+1 проблемы в UseCase:
Использование между слоями приложения:
Кеширование внешних API запросов:
Комбинирование с кешированием:
Когда использовать мемоизацию
✅ Используйте memo() когда:
- Action вызывается несколько раз в одном запросе с одинаковыми аргументами
- Нужно избежать дублирования запросов к БД/API/дублирования выполнения сложной логики или вычислений
- Action используется в циклах или рекурсивно
- Action вызывается из разных слоёв (Controller -> UseCase -> другие Actions)
- Результат нужен только на время текущего запроса
❌ Не используйте memo() когда:
- Action вызывается только один раз за запрос
- Результат должен быть свежим при каждом вызове
- Action имеет побочные эффекты (отправка email, запись в БД)
- Нужно сохранить результат между разными HTTP запросами (используйте
remember())
Производительность
Мемоизация добавляет минимальный overhead, однако облегчает рутинные действия и избавляет от дублирования запросов к БД/Кешу. Расценивайте это как своего рода цену за удобство. Пример:
- Без
memo(): ~0 overhead - С
memo()(первый вызов): ~5-10μs (генерация хеша) - С
memo()(повторные вызовы): ~1-2μs (проверка массива) - С
memo(forceEvents: true): +50-100μs (запуск событий) -
Оптимизация событий: По умолчанию для мемоизированных результатов события не запускаются, что экономит приблизитльно ~50-100μs на каждый повторный вызов, в зависимости от логики которую вы заложили. Используйте
forceEvents: trueтолько когда действительно необходимо вызывать события повторно при повторном вызове экшена.
Комплексное использование
Все возможности можно комбинировать:
Лучшие практики
1. Один Action - одно действие, UseCase - сценарий
2. Используйте абстракции для взаимозаменяемых Actions (DIP)
3. Используйте типизацию
3. UseCase для агрегирования Actions
UseCase - это тот же Action, но предназначенный для координации множества других Actions в единый сценарий:
Преимущества UseCase:
- Все Actions в UseCase выполняются в единой транзакции
- UseCase можно кешировать целиком
- События отслеживают весь сценарий
- Переиспользуемая бизнес-логика
Dependency Inversion Principle (SOLID)
Actions загружаются через Laravel Service Container (app(static::class)), что позволяет применять принцип инверсии зависимостей:
Подход 1: Подмена через абстрактные классы
Подход 2: Подмена конкретного класса
Подход 3: Регистрация по строковым ключам (менее предпочтительный, но возможный способ)
Внедрение зависимостей через конструктор
Глобальная подмена для тестирования
Условная подмена
Преимущества DIP в Actions
- Тестируемость: легко подменять реализации в тестах
- Гибкость: можно менять реализацию без изменения UseCase
- Изоляция: UseCase зависят от абстракций, а не конкретных классов
- Переиспользование: разные реализации одной абстракции
- Feature Flags: включать/выключать функциональность через контейнер
Почему не интерфейсы?
⚠Важно: Нельзя использовать интерфейсы с конкретной сигнатурой run(), так как базовый контракт Action уже определяет run(...$args): mixed с variadic параметрами. Любой другой интерфейс с конкретной сигнатурой будет несовместим.
Решение: Используйте абстрактные классы или подменяйте конкретные классы через контейнер.
Хелперы
Пакет предоставляет удобные хелперы:
action() - Быстрое выполнение Action
usecase() - Быстрое выполнение UseCase
action_with() - Action с конфигурацией
Позволяет сконфигурировать Action перед выполнением через callback:
usecase_with() - UseCase с конфигурацией
Позволяет сконфигурировать UseCase перед выполнением через callback:
generate_args_hash() - Генерация хеша аргументов
Функция для генерации MD5 хеша из массива аргументов. Используется внутри пакета для мемоизации и кеширования, но доступна и для внешнего использования:
Почему она появилась?:
- Использует
json_encodeдля производительности (обычно быстрее чемserialize) - Автоматический fallback на
serializeдля сложных объектов (Closure, Resources) - Возвращает MD5 хеш для компактности ключей
- Чтобы не дублировать в нескольких местах (Кеширование, мемоизация) вынесена в отдельный хелпер
Использование в контроллерах
Хелперы особенно удобны в контроллерах и сервисах:
4. Кешируйте тяжелые операции
Лицензия
GPL-2.0-only
Автор
Vladimir Pyankov (aka LeMaX10)
- Email: [email protected]
- Website: https://pyankov.pro