ratiborro/laravel-scopes

Generating local scopes for model in existing file by database columns

v1.0.6 2021-03-08 21:25 UTC

This package is auto-updated.

Last update: 2024-06-11 23:25:18 UTC


README

Пакет для максимально простого создания локальных скоупов моделей по колонкам в базе данных.

Можно автоматически генерировать скоупы по имеющимся колонкам таблицы модели, по желанию исключая колонки по именам (для всех или для конкретных моделей), настраивая как угодно генерацию имён скоупов для каждого типа данных БД, а также как угодно настраивая логику каждого генерируемого скоупа (если не подойдёт поведение по-умолчанию).

Installation

Установите пакет:

composer require ratiborro/laravel-scopes --dev

Usage

Создайте скоупы для модели одной простой командой:

php artisan make:scopes ModelName

Команда найдёт файл модели (в пределах базовой директории моделей из конфига), сгенерирует имена для всех будущих скоупов по колонкам в БД (с учётом переименований и исключений колонок, а также логики генерации имен скоупов), заполнит их логикой из шаблонов, а затем импортирует их в модель (сразу после существующих скоупов при их наличии, иначе - в конец класса модели).

Для каждой колонки можно создавать неограниченное количество скоупов (с некоторым ограничением именования, если скоупов больше одного на колонку).

Вы можете менять созданные скоупы как захотите без страха потери ваших изменений, так как пакет не затрагивает уже созданные скоупы.

Configuration

Публикация конфига:

php artisan vendor:publish --tag=laravel-scopes-config
  • Настраивайте корневую папку хранения моделей
  • Настраивайте как угодно логику генерации имен скоупов
  • Колонки: исключайте ненужные, изменяйте имена, укажите тип по-умолчанию для самых необычных типов колонок
  • Шаблоны (stubs): настраивайте путь сохранения шаблонов и карту преобразования типов (например, чтобы для типа bigint генерировался скоуп по такому же шаблону, что и int)

Публикация шаблонов (stubs) различных объектов Laravel:

php artisan stub:publish

Публикация шаблонов создаваемых скоупов (для их дальнейшего редактирования или создания новых):

php artisan vendor:publish --tag=laravel-scopes-stubs

Examples

Например, есть модель User с колонками id, name, email, is_admin, lovely_word, team_id, verified_at, created_at, updated_at.

Если вас устраивает поведение по-умолчанию или вы уже настроили пакет под себя, никаких действий, кроме выполнения команды make:scopes не требуется. Но мы же не ищем лёгких путей, верно? :)

Сначала опишем свои хотелки:

  1. Допустим, нам не нужна генерация скоупов для колонок id, created_at и updated_at.

  2. Для колонок с внешними ключами (заканчивающихся на *_id) мы хотим такой стиль именования, чтобы было понятно, что это отношения к другим сущностям: например для колонки team_id мы желаем иметь скоуп scopeOfTeam.

  3. Для булевых колонок мы хотим следующий стиль именования без is (если есть): is_admin => scopeAdmin

  4. Конкретно для колонки name мы хотим видеть скоуп scopeNamed, просто потому что нам так больше нравится.

  5. Название lovely_word осталось в базе по историческим причинам (как и другие похожие), поэтому для всех колонок начинающихся на lovely_* мы хотим изменить слово в скоупе на favorite (lovely_word => scopeFavoriteWord)

  6. Для datetime колонок мы хотим иметь по два скоупа: один для точного поиска по дате и времени, а другой для поиска только по дате.

  7. Для остальных колонок нас устраивает стиль именования camelCase: scopeColumnName.

Теперь, чтобы реализовать все наши желания, нам необходимо выполнить следующие действия:

  • Генерацию скоупов для колонок id, created_at и updated_at мы просто отключаем, перечислив эти колонки в конфиге:

    'columns' => [
      ...
      'exclude_columns' => ['id', 'created_at', 'updated_at', 'deleted_at'],
      ...
    ],
    
  • Кейсы 2 и 3 уже вшиты как поведение по-умолчанию (привилегия автора), при необходимости стиль именования можно изменить.

  • Колонку name мы указываем в конфиге в словаре

    'columns' => [
      ...
      'dictionary' => [
          'name' => 'named'
      ]
      ...
    ],
    
  • Для написания индивидуальной логики именования колонки lovely_word мы создадим класс CustomScopeNameGenerator, который будет расширять класс Ratiborro\LaravelScopes\Helpers\ScopeNameGenerator, генерирующий имена скоупов. Так как тип колонки строка, мы переопределим метод getStringScopeName и допишем туда логику наподобие:

      if (Str::startsWith($name, 'lovely_')) {
          return 'favorite_' . Str::after($name, 'lovely_');
      }
    

    Также важно не забыть указать наш класс в конфиге:

      'scopes' => [
          'nameGeneratorClass' => \App\YourPath\ScopeNameGenerator::class
      ],
    
  • Для того, чтобы сделать 2 скоупа для datetime колонок, нам нужно:

    • создать шаблон datetime.stub. По-умолчанию такого шаблона нет, так как для date и datetime колонок скоупы генерируются по одному шаблону и в конфиге прописан маппинг с datetime на date. Можно просто скопировать шаблон date.stub и сохранить как datetime.stub, после чего удалить маппинг 'datetime' => 'date', из конфига, чтобы брался шаблон по имени типа колонки.
    • изменить логику в шаблоне, например, на такую:

            public function scope{{$name}}($q, $value)
            {
                return $q->where('{{$column}}', $value);
            }
            
            public function scope{{$name}}Date($q, $value)
            {
                return $q->whereDate('{{$column}}', $value);
            }
      

      где $name - это имя скоупа, созданное генератором имён скоупов, $column - оригинальное название колонки

      Тип значения специально не указываем, чтобы можно было прислать как строку даты, так и, например, Carbon объект.

      На выходе это даст скоупы с названиями scopeVerifiedAt и scopeVerifiedAtDate. Ограничением в именовании выступает лишь общая часть названия, но она может быть какой угодно, всё ограничивается лишь вашей фантазией.

  • Для остальных колонок мы не делаем никаких специальных действий, и это создаст нам "стандартные" скоупы без ухищрений

На выходе мы получим скоупы с именами: scopeNamed, scopeEmail, scopeAdmin, scopeFavoriteWord, scopeOfTeam, scopeVerifiedAt и scopeVerifiedAtDate, в которых будет описана та логика, которая нам необходима (из шаблонов).

Troubleshooting

  1. Ошибка Model not exists: убедитесь, что namespace модели совпадает с тем, что прописан в конфиге.

    Например, если в конфиге прописан namespace App\Models, то вы можете вызывать модель через её название: php artisan make:scopes User, если она находится непосредственно в пространстве имён Models, или же через php artisan make:scopes SubdirectoryName/User, если модель находится в подпапке и имеет другой namespace.

    Если модели лежат в другой папке, просто измените конфиг models.base_namespace.

  2. Ошибка Fails to get model table columns: убедитесь, что таблица для данной модели уже существует (миграция на создание таблицы уже выполнена). Если ошибка остаётся, пожалуйста, обратитесь по нижеуказанным контактам.

Contacts

Author: Ratibor Korobin