Бывает так, что в нашем приложении есть две модели (а иногда и больше), которые имеют одинаковые атрибуты, но отличаются некоторыми другими. Возьмем к примеру мой сайт Podcast, над которым я сейчас работаю: мне нужны модели Podcast, Song и Soundtrack.
Как вы догадались, все эти три модели имеют общие свойства: каждая из них представляет собой отдельный аудиофайл с битрейтом, именем, форматом, длиной, тегами и так далее. В то же время у них есть свойства, несовместимые друг с другом: подкаст принадлежит шоу, песня — альбому, а саундтрек — фильму.
Обычно мы сделали бы для каждой модели свою таблицу, повторяя общие свойства, а затем добавляя уникальные. Благодаря Laravel Eloquent ORM вам даже не нужно задумываться об этом.
РЕШЕНИЕ: Одна таблица, чтобы править всеми
Вам не нужно делать для каждой модели свою таблицу. Вам нужна только одна таблица с именем audios, у который столбец type будет определять модель, а json-столбец (или text) под названием properties будет хранить их свойства, которыми они отличаются друг от друга. Вы можете указать Laravel проиндексировать этот столбец, для лучшей производительности при фильтрации, или сделать составной индекс.
Schema::create('audios', function (Blueprint $table) { // ... $table->string(’type’); $table->json(’properties’); });
Как только вы создадите таблицу, вам нужно будет создать три модели: Podcast, Song и Soundtrack , которые наследуют абстрактную модель Audio. Магия будет в Глобальных Заготовках (Global scopes — Скоупы).
Скоупы в каждой модели позволят разделить их в основной таблице, просто добавляя условие where. Этим мы отфильтровываем строку, идентифицирующую модель. В случае с Podcast в запросе будет «все аудиозаписи, где типом является подкаст».
public function boot() { parent::boot(); static::addGlobalScope('podcast', function (Builder $builder) { $builder->where('type', 'podcast'); }); }
Вы можете использовать столбец properties для хранения ваших данных, таких как show, album или movie, или же весь массив свойств. Это уже будет зависеть от вашего приложения, но лично я использую JSON, когда мне нужна куча неотфильтрованных данных, и оставляю в столбцах свойства, которые нужно будет отфильтровать с помощью SQL.
Вы также можете использовать гипотетический trait IsAudioFile в трех других классах.
Абстрактный класс или трейт должны содержать все общие методы для классов, использующих его. Поскольку вы можете переопределить методы и свойства, это нормально, если одной модели нужно что-то особенное.
Преимущества?
- Если вам нужно добавить еще одну модель, например «AudioLog», то вы можете просто создать Eloquent Model и добавить глобальный Скоуп для фильтрации по типу audiolog . Нет необходимости редактировать базу данных.
- Поскольку у вас всего одна таблица, то нет проблемы с отношениями. В худшем случае вы всегда сможете использовать полиморфные отношения.
- Использование json-столбца позволяет вам работать с чем захотите. Все последние версии MySQL, MariaDB, PostgreSQL и SQL Server позволяют фильтровать по JSON значения.
Автор: Italo Baeza
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.