Pull to refresh

STI — одна таблица и много моделей

Reading time 3 min
Views 27K
Вчера, в заметке про полиморфные связи в комментариях был упомянут паттерн STI. Как выяснилось, не все знают что это такое, как работает и зачем нужно. Решил восполнить этот информационный пробел и вкратце рассказать об этом шаблоне проектирования и его реализации в Рельсе.

STI (Single Table Inheritance) — паттерн проектирования, который позволяет перенести объектно-ориентированное наследование на таблицу реляционной базы данных. В таблице БД должно присутствовать поле идентифицирующее название класса в иерархии. Зачастую, в том числе в RoR, поле называют type.

Таким образом, мы можем иметь одну таблицу и несколько типов объектов (моделей), которые будут в ней храниться. В случае с вышеупомянутой хабразаметкой — это одна таблица постов, которая хранит посты разных типов: ссылка, подкаст, статья, перевод и т.д.

Дабы не усложнять себе жизнь, в этой статье мы рассмотрим более простой пример: несколько типов пользователей с разными полномочиями и любой другой бизнес-логикой. Пусть это будут: администратор, менеджер и рядовой пользователь.

Приступим.

Определяемся с задачей


Допустим, в нашем приложении (например интернет-магазине) нам требуется реализовать иерархию пользователей с различными полномочиями и, возможно, дополнительной логикой. Пусть это будут: администратор, менеджер и рядовой пользователь. Вполне логично для каждого создать отдельную модель: Administrator, Manager, User. Учетные записи всех пользователей хранятся в одной таблице базы данных.

Создаем структуру БД


Нам требуется одна таблица, в целях максимального упрощения в ней будут два поля: username и password. Также, нам нужно как-то хранить тип пользователя, по умолчанию в Рельсе для этих целей используется поле под названием type.

В итоге получаем следующую структуру:
CREATE TABLE users (<br> id INT NOT NULL AUTO_INCREMENT,<br> username VARCHAR(20) NOT NULL UNIQUE,<br> password VARCHAR(32) NOT NULL,<br> type VARCHAR(40) NOT NULL,<br> PRIMARY KEY (id)<br>);

Разумеется, правильнее было бы написать миграцию, но это уже выходит за рамки данной статьи. Будем считать это заданием для самостоятельной работы :)

Создаем модели


Создаем модель рядового пользователя:
class User < ActiveRecord::Base<br>end

Следует иметь ввиду: мы явно не указываем название таблицы для этой модели т.к. в данном случае название таблицы и модели соответствуют соглашениям, принятым в Рельсе.

Создаем модель менеджера, наследуемся от базового пользователя:
class Manager < User<br>end

Создаем модель администратора, наследуемся от менеджера:
class Administrator < Manager<br>end

Для упрощения примера, мы не усложняем модели дополнительной логикой, которая могла бы нам понадобиться в реальном приложении. Если требуется вы всегда можете добавить специфический код для каждого типа пользователей в соответствующую модель.

Используем!


Теперь в контроллере мы можем делать выборки из моделей привычным способом:
users = User.find(:all)
В данном случае мы получим всех пользователей, включая рядовых, менеджеров и администраторов.

Но если мы сделаем выборку из модели Administrator, то получим только администраторов:
administrators = Administrator.find(:all)

Вместо заключения


В данной заметке мы пропустили многие аспекты, которые не имеют прямого отношения к основной теме от написания миграций до использования генераторов. Также упомянуты далеко не все возможности, которые предоставляет Рельсовый ActiveRecord при использовании STI. Интересующиеся могут изучить эти моменты самостоятельно.

Single Table Inheritance у Мартина Фаулера
Документация по ActiveRecord в Ruby on Rails
Tags:
Hubs:
+3
Comments 8
Comments Comments 8

Articles