Полиморфные объекты

Полиморфные объекты меняют своё поведение, внешний вид в зависимости от обстоятельств. Для хранения полиморфнных объектов разных типов используется одна таблица, при это тип объекта определяется полем type.

Например, в таблице users в поле type может находится значение admin, author и reader. При этом каждый из пользователей имеет общие поля (логин, пароль), но вывод информации о пользователях рахных типов разный.

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

Также можно использовать свойство template, которое позволяет задать шаблон объекта при попытке вывести его как строку (или при помощи метожа ->show()). Подробнее про метод show() и вывод объекта как строку можно узнать в разделе Вывод объекта страницы Active_record

Ниже перечислены типичные случаи применения полиморфных объектов, свойства type и функции route_to()

Виджеты

Допустим, есть задача - выводить в левой части страницы виджеты (формы связи, текстовые вставки, либо меню). Допустим, для этих целей заранее заготовлены шаблоны/функции fosv_widget, text_widget, menu_widget.

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

Для этого пишутся функции получения списка виджетов и вывода виджета:

//Получение массива виджетов из таблицы widgets
function leftpanel_widgets()
{
    return d()->Widget->find_by_position('left')->all;
}

//Функция ничего не делает, и перенаправляет вывод в дальнейший обработчик в текущей цепочке.
function widget($param)
{
    d()->route_to(d()->widget->type);
}

В роутер записываются дополнительные именованные маршруты:

fosv widget fosv_widget
text widget text_widget
menu widget menu_widget

В шаблон вставляется следующий код:

<foreach leftpanel_widgets() as widget>
    <div class="widget">

        <div class="widget_header"> {widget.title} </div>

        <div class="widget_container">
            {{widget}}
        </div>

    </div>
</foreach>  

Итого в зависимости от поля type (fosv, text, menu) выполнится один из трёх (или более) виджетов.

Таким образом, подход гибкий (не захардкожены названия виджетов), расширяемый, не содержит лишней логики внутри шаблона, не содержит вообще лишней логики (if отсутствуют).

Поставщики и заказчики

Допустим, на странице /users/252 должна отображаться информация о пользователе c id = 252, причём сами пользователи могут быть двух типов: заказчики (customers) и поставщики (suppliers). Все они находятся в таблице users, имеют логины и пароли, но разные шаблоны вывода. При отображении страницы поставщика необходимо вывести список выполненных заказов (таблица orders, пользователь определяется по user_id в этой таблице, один пользователь может иметь несколько заказов), при отображении страницы заказчика необходимо отобразить другую информацию о компании (таблица companies, компания определяется по полю company_id в таблице users, один пользователь представляет только одну компанию, компанию могут представлять несколько пользователей).

Шаблон страницы user_show_customer.html:

<h1>{user.title}</h1>
Тут информация о заказчике, свойства компании
{company.title}

Шаблон страницы user_show_supplier.html:

<h1>{user.title}</h1>
Тут информация о поставщике, список заказов
<foreach orders as order>
    <li>{order.title}</li>
</foreach>

Роутер изначально переносит на users_show():

/users/     content users_show 
customer    content user_show_customer user_show_customer_tpl
supplier    content user_show_supplier user_show_supplier_tpl

Функции:

function users_show()
{
    d()->user = d()->User->find(url(2));
    d()->route_to(d()->user->type);
}

function user_show_supplier()
{
    d()->orders = d()->user->orders;
}

function user_show_customer()
{
    d()->company = d()->user->company;
}

Получать d()->orders не обязательно, но в примере могут быть более сложные методы обработки данных. Таким образом, route_to перенаправит систему на другую цепочку, содержащую получение данных и их вывод.

Продавцы, товары и покупатели

Допустим, каждый товар (stuff) имеет два столбца, указывающие на двух типов пользователей, хранящихся в users. Например, buyer и seller. user_id использовать нельзя, т.к. связей две.

Для этого указываются значения столбцов seller_id и buyer_id. Seller и buyer — значения свойства type таблицы users. Таким образом, для обращения к продавцу стоит использовать конструкцию:

print d()->Stuff->find(1)->seller->login;

Или, для вывода в шаблоне:

<foreach stuffs as stuff>
    Продавец: {stuff.seller.login} | Покупатель: {stuff.buyer.login}
</foreach>

comments powered by Disqus