Полиморфные объекты меняют своё поведение, внешний вид в зависимости от обстоятельств. Для хранения полиморфнных объектов разных типов используется одна таблица, при это тип объекта определяется полем 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>