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

    Вчера, в заметке про полиморфные связи в комментариях был упомянут паттерн 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
    Метки:
    • +3
    • 14,5k
    • 8
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 8
    • +3
      Для полноты картины было бы здорово привести пример заполнения таблицы данными и результаты выборок.
      • +2
        Хе, моя статья сподвигла написать уже 2 статьи в блог Ruby on Rails. Это ли не повод радоваться?!
        Спасибо за интересный материал
        • 0
          Вам спасибо за положенное начало :) Будем надеяться: эти две статьи не последние.
        • +1
          Subaru Tecnica International!
          • 0
            Можно указать ещё такой момент:
            так как таблица одна, а моделей несколько то для всех атрибутов будут созданы соответствующие поля в таблице.
            В указанном примере никаких дополнительных атрибутов в моделях нету поэтому их нет и в таблице, если же у администраторов и менеджеров будут какие то свои непересекающиеся атрибуты, то понадобится заводить эти поля в таблице.
            STI позволяет как видно из примеров элегантно обойтись одной таблицей для схожих моделей,
            а в чём подводные камни?
            • 0
              • 0
                За ссылку спасибо :)

                Только я все из своей головы писал, с «оригиналом» не знаком, а если бы переводил — оформил бы как перевод.
              • 0
                Спасибо за наводку. Весь день мучился над поиском решения.

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