IO v6. Model и работа с ними
Описание
Model (модель) - файл логики, работающий (чаще всего) в связке с class'ом таблицы базы данных.
Model формирует API приложения.
Модель - это объект в системе, который отвечает за логику работы приложения. Модель отвечает за проверку прав, выполнение CRUD операций, прорисовку таблиц, форм. К методам модели можно обращаться из других проектов. Модель связывает пользовательский интерфейс и данные, которые находится в базе данных.
ВАЖНО!
Модель на экран ничего не должна выводить. Она должна либо вызывать функцию render, либо возвращать данные через return.
Закрывать тег ?> в файле не требуется.

Содержимое файла
<?php
class ExampleModel extends IOModel4
{
/**
* Разрешения на выполнение методов класса
*/
public static function rcp_rules()
{
return [
'static' => [
// предустановленные функции
//'actionShowNiceList' => 1,
//'actionShowNiceFilter' => 1,
//'actionShowNiceFilterField' => 1,
//'actionNiceData' => 1,
//'actionShowAdd' => 1,
//'actionAdd' => 1,
//'actionShowEdit' => 1,
//'actionEdit' => 1,
//'actionShowDelete' => 1,
//'actionDelete' => 1,
//'actionEditField' => 1,
// actionAbc это вызов пользовательской функции
'actionAbc' => 1,
'actionGetData' => 1,
'actionGetReport' => 1,
],
];
}
/**
* Структура полей таблицы класса базы данных
*/
public static function getStruct($params)
{
$fields = [
'pkid' => [
'type'=> 'hidden',
'showAdd'=> false,
'hidden'=> true,
'isPrimary'=> true,
],
'typeInt' => [
'label' => 'Int',
'type' => 'number',
'filter' => true,
],
'typeLong' => [
'label'=>'Long',
'type'=>'select',
'options' => [
'ноль',
'один',
'два',
'три',
'четыре',
],
'filter' => true,
],
'typeDouble' => [
'label' => 'Double',
'type' => 'input',
'filter' => true,
],
'typeBool' => [
'label' => 'Bool',
'type' => 'checkbox',
'filter' => true,
],
'typeString' => [
'label' => 'String',
'type' => 'input',
'filter' => true,
],
'typeDate' => [
'label' => 'Date',
'type' => 'date',
'date_format' => 'd-m-Y',
'jsDate_format' => 'dd-mm-yyyy',
'date_output_timezone' => $ioSession->user->timezone,
'filter' => true,
],
];
return [
'fields' => $fields,
'primary' => [
'pkid',
],
];
}
/**
* Настройки model
*/
public static function getParams($params)
{
$result = [
'class' => 'app.class.test', // указываем путь до класса таблицы
'model' => 'app.model.test', // указываем путь до модели. если у проекта нет IOPROJECT apiname, используем app
'classSearch' => 'app.class.test.dbsearch3', // указываем путь до класса таблицы и его функции dbsearch3
'forceDelete' => 1, // 0 - помечать запись как удаленную, вместо удаления на совсем
'renderForm' => 'io.view.nicetable.niceForm',
'renderFilterElem' => 'io.view.nicetable.niceFilterElem',
'renderFilter' => 'io.view.nicetable.niceFilter',
];
return $result;
}
/**
* События и разрешения model
*/
public static function event($event = '', $params = [])
{
$struct = xarr($params, 'struct', []);
$object = xarr($params, 'object', []);
$form = xarr($params, 'form', []);
$p = xarr($params, 'params', []);
if($event == 'init')
{
return EIO_OK;
}
elseif($event == 'searchFilter')
{
$params['xfilter'] = [
//'type' => 2,
//'site_pkid' => $site_pkid,
];
$params['defaultFilter'] = [
'isDeleted' => false,
];
$filter = parent::event($event, $params);
return $filter;
}
elseif($event == 'getObjectFilter')
{
$params['xfilter'] = [
'isDeleted' => false,
];
$filter = parent::event($event, $params);
return $filter;
}
elseif($event == 'searchOrder')
{
$params['order'] = array(
'ugmtimeAdd' => 'desc',
);
$order = parent::event($event, $params);
return $order;
}
elseif($event == 'search_post')
{
parent::event($event, $params); // вызываем старый event
}
return parent::event($event, $params);
}
/**
* Пример пользовательской функции
*/
public static function doAbc($result, $params)
{
$result->ret_params = $params;
$result->a = xarr($params, 'a', 456);
// логика...
$result->error_code = EIO_OK;
__exit;
return $result;
}
}
Методы model
| Название | Описание |
|---|---|
rcp_rules() | Разрешает вызов callAction через php, twig, javascript. |
getStruct() | Возвращает поля модели. Эта функция отвечает какие поля выводить в таблице, форме редактирования и добавления. |
getParams() | Возвращает параметры модели: название модели. Класса, рендер формы, таблицы. Эти параметры могут быть переопределены get параметрами, либо через params при вызове IOCallAction. |
event() | Процедура callback событий. |
actionShowAdd() | Вызвать окно добавления (стандартная функция IOModel4). |
actionAdd() | Добавить запись (стандартная функция IOModel4). |
actionShowEdit() | Вызвать окно редактирования (стандартная функция IOModel4). |
actionEdit() | Редактировать запись (стандартная функция IOModel4). |
actionShowDelete() | Вызвать окно удаления (стандартная функция IOModel4). |
actionDelete() | Удалить запись (стандартная функция IOModel4). |
actionShowNiceList() | Рисует табличку (стандартная функция IOModel4). |
actionNiceData() | Получить данные таблицы (стандартная функция IOModel4). |
actionExportExcell() | Выводит код excell файла (стандартная функция IOModel4). |
actionShowFilterField() | Рисует элемент фильтра (стандартная функция IOModel4). |
actionShowFilter() | Рисует сам фильтр (стандартная функция IOModel4). |
actionGetData() | Получить чистые неотформатированные search_post данные (стандартная функция IOModel4). |
actionShowRestore() | Вызвать окно о восстановлении объекта. |
actionRestore() | Восстановить объект. |
rcp_rules()
Возвращает список разрешенных на выполнение методов класса.
Пример:
/**
* Разрешения на выполнение методов класса
*/
public static function rcp_rules()
{
return [
'static' => [
// предустановленные функции
//'actionShowNiceList' => 1,
//'actionShowNiceFilter' => 1,
//'actionShowNiceFilterField' => 1,
//'actionNiceData' => 1,
//'actionShowAdd' => 1,
//'actionAdd' => 1,
//'actionShowEdit' => 1,
//'actionEdit' => 1,
//'actionShowDelete' => 1,
//'actionDelete' => 1,
//'actionEditField' => 1,
// actionAbc это вызов пользовательской функции
'actionAbc' => 1,
'actionGetData' => 1,
'actionGetReport' => 1,
],
];
}
В еще более ранних версиях фреймворка дополнительно в return передавалось следующее: (ныне устарело и не требуется)
'object' => [
//'*' => 1,
],
getStruct()
Возвращает структуру данных модели.
Параметры структуры:
| Название | Значение по умолчанию | Описание |
|---|---|---|
type | null | Тип поля |
label | null | Заголовок поля |
value | null | Значение, которое будет отображаться в форме, обычно перезаписывается данными из БД |
default | null | Значение по умолчанию, если value = null |
showAdd | true | Разрешить показ в форме добавления |
showEdit | true | Разрешить показ в форме редактирования |
hidden | false | Скрыть столбец из niceTable |
isShow | true | ?? |
infoHidden | false | ?? |
infoHiddenMobile | false | скрыть в мобильном приложении при выводе |
isPrimary | false | Первичный ключ или нет, влияет на то будет ли он отображаться в форме удаления или нет, и подставляться в различные ссылки для идентификации объекта |
options | null | Параметр для типа select |
date_format | null | Параметр для типа date. Формат данных для отображения в input |
jsDate_format | null | Параметр для типа date. Формат данных для bsdatepicker |
date_output_timezone | null | Параметр для типа date. Временная зона клиента |
entity | null | Параметр для типа select, источник данных. |
foreignKey | null | Параметр для типа select, внешний ключ |
required | 0 | Обязательное поле или нет |
Типы данных модели:
| Тип данных | Описание |
|---|---|
input | строка |
hidden | скрытое поле, применяется для isPrimary |
date | дата |
email | емаил |
phonenumber | номер телефона |
number | число |
select | список |
file | файл |
money | денежный тип |
raw-data | Текст, выводимый в форме для справки Дополнительные параметры:
|
tagit | теги |
Примеры использования параметров
Input
$fields = [
'login' => [ // логин
'type' => 'input',
'label' => 'Логин',
'filter' => true,
'fullText' => 1,
],
];
foreach(IOCore::$languages as $lang => $name)
{
$fields['name.'.$lang] = [
'type' => 'input',
'label' => 'Название ('.$lang.')',
'filter' => true,
'fullText' => 1,
];
}
Hidden
$fields = [
'pkid' => [
'type' => 'hidden',
'label' => 'pkid',
'default' => '',
'isPrimary' => true,
'showAdd' => false,
'hidden' => true,
],
'site_pkid' => [
'type' => 'hidden',
'label' => 'site_pkid',
'default' => '',
'isPrimary' => true,
'showAdd' => false,
'hidden' => true,
],
];
Date
$fields = [
'date_begin' => [
'type' => 'date',
'label' => 'Дата начала олимпиады',
'date_format' => 'Y-m-d',
'jsDate_format' => 'yyyy-mm-dd',
'date_output_timezone' => $ioSession->user->timezone,
],
'date_end' => [
'type' => 'date',
'label' => 'Дата окончания олимпиады',
'date_format' => 'Y-m-d',
'jsDate_format' => 'yyyy-mm-dd',
'date_output_timezone' => $ioSession->user->timezone,
],
];
Select
Способ 1. Получение параметров через IOCore::call.
$tasks = [];
$r = IOCore::call(
'app.class.olymp.tasks.dbsearch2',
[
'filter' => [
'site_pkid' => $site_pkid,
],
'out' => 'array',
]
);
$res = xarr($r, 'res', []);
foreach($res as $obj)
{
$pkid = xarr($obj, 'pkid');
$name = xarrj($obj, 'name.ru');
$tasks[$pkid] = $name;
}
$fields = [
'task_pkid' => [
'type' => 'select',
'label' => 'Задание',
'default' => '',
'showEdit' => false,
'options' => $tasks,
'filter' => true,
],
];
Способ 2. Получение параметров через entity.
$fields = [
'task_pkid' => [
'subject_pkid' => [
'type' => 'select',
'label' => 'Предмет',
'default' => '',
//'options' => $subjects,
'entity' => 'app.model.olymp.subjects.getData',
'foreignKey' => 'name.ru',
'params' => [
'site_pkid' => $site_pkid,
],
'filter' => true,
'showAdd' => false,
'showEdit' => false,
],
],
];
Способ 3. Получение параметров через $ioEnum.
global $ioEnum;
$fields = [
'isDeleted' => [
'type' => 'select',
'label' => 'Удален',
'options' => $ioEnum['boolean'],
'filter' => true,
'showAdd' => false,
'showEdit' => false,
'hidden' => true,
],
];
File
$fields = [
'logo' => [
'label' => 'Логотип сайта',
'type' => 'file',
//'required' => 1,
'max_size' => 512 * 1024,
'ext' => ['png', 'jpg', 'jpeg', 'gif'],
'path' => '/files/sites/%pkid%/',
'file' => 'logo.{ext}',
],
];
getParams()
Данная функция задает параметры модели.
class– какой класс использовать для поиска, добавления, сохранения и удаления данных.model– ссылка на свою модель.classSearch– метод поиска данных класса.forceDelete– при удалении удалять данные, либо ставитьisDeleted. ЕслиforceDelete = 0, то ставитьisDeleted.buttonsиbuttons2– какие кнопки будут прорисовываться вniceTable.buttons– слева,buttons2- справа.formEditButtons,formAddButtons,formDeleteButtons– какие кнопки будут прорисовываться вniceTable. Вevent search_postможно запретить вывод для определенных строк.
public static function getParams($params)
{
global $ioEnum;
return [
'class' => 'app.class.olymp.answers',
'model' => 'eop.model.olymp.answers',
'classSearch' => 'app.class.olymp.answers.dbsearch3',
'forceDelete' => 0,
//'renderForm' => 'app.view.olymp.answers.addedit',
'buttons' => [
'edit' => [
'title' => 'Изменить %name%',
'action' => 'app.model.olymp.answers.showEdit/?pkid=%pkid%',
'icon' => '/img/edit.png',
],
'delete' => [
'title' => 'Удалить %name%',
'action' => 'app.model.olymp.answers.showDelete/?pkid=%pkid%',
'icon' => '/img/del.png',
],
],
'formEditButtons' => [
'submit_and_close' => [
'label' => 'Изменить',
'event' => 'form_submit_and_close',
'type' => 'single',
'style' => 'primary',
'extraClass' => 'btn btn-primary',
],
],
];
}
Пример переопределения параметров при вызове IOCallAction в Twig.
{% set res = ioCallAction(
'app.model.olymp.answers.showNiceList',
{
'niceTableID': niceTableID,
'niceFilterID': niceFilterID,
'onEvent' : 'onEvent'~niceTableID,
'site_pkid' : site.id,
'buttons' : {
'accept' : {
'title' : 'Рассмотреть ответ %name.'~currentlang~'%',
'href': '/cabinet/olymp/answers/accept?pkid=%pkid%&site_pkid='~site.id,
'icon' : '/img/common/edit.png',
},
},
'buttons2' : {
'delete' : {
'title' : 'Удалить %name.'~currentlang~'%',
'action' : 'app.model.olymp.answers.showDelete/?pkid=%pkid%&site_pkid='~site.id,
'icon' : '/img/common/del.png',
},
'restore' : {
'title' : 'Удалить %name.'~currentlang~'%',
'action' : 'app.model.olymp.answers.showRestore/?pkid=%pkid%&site_pkid='~site.id,
'icon' : '/img/common/restore.png',
},
},
}
) %}
Пример переопределения параметров при вызове IOCallAction в php.
self::setLayout('io.view.cabinet.index');
self::renderAction(
'app.model.olymp.tasks.showAdd',
[
'site_pkid' => xarr($ioPage->site, 'id'),
'renderForm' => 'app.view.olymp.tasks.addedit',
]
);
Пример запрета вывода кнопок для определенных строк:
foreach($data2 as $i => $row)
{
$user_pkid = xarr($row, 'user_pkid');
$judge_user_pkid = xarr($row, 'judge_user_pkid');
$user = xarr($g_users, $user_pkid);
$judge = xarr($g_users, $judge_user_pkid);
if($user != null)
{
$ioData['data'][$i]['user_pkid'] = xarr($user, 'sname') .' '. xarr($user, 'fname') .' '. xarr($user, 'lname') . ' /'. xarr($user, 'login');
}
if($judge != null)
{
$ioData['data'][$i]['judge_user_pkid'] = xarr($judge, 'sname') .' '. xarr($judge, 'fname') .' '. xarr($judge, 'lname') . ' /'. xarr($judge, 'login');
}
if(xarr($row, 'isCheck', false))
{
$ioData['data'][$i]['buttons']['accept']['value'] = 0;
$ioData['data'][$i]['buttons']['delete']['value'] = 0;
$ioData['data'][$i]['buttons']['restore']['value'] = 0;
$ioData['data'][$i]['buttons2']['delete']['value'] = 0;
$ioData['data'][$i]['buttons2']['restore']['value'] = 0;
}
}
event()
Стандартная функция event выглядит следующим образом:
public static function event($event = '', $params = [])
{
global $ioSession, $ioData, $ioProjects;
$p = call_user_func_array ([get_called_class(), 'params'], [$params]);
$model = xarr($p, 'model');
$p = xarr($params,'params');
// Описание всех событий
return parent::event($event, $params);
}
ВАЖНО!
Функция всегда должна в конце делать вызов return parent::event($event, $params);.
Список событий:
| Название | Описание |
|---|---|
init | Вызывается всегда, когда дается управление модели в функцию с префиксом do |
search_post | Вызывается, когда происходит поиск данных через функции getData, niceData. Нужен для обработки полученных данных через массивы $ioData[‘data’] и $ioData[‘dat2’] |
add | Вызывается когда происходит вызов функции showAdd |
add_pre | Вызывается перед тем как в базу добавить запись. Здесь можно предварительно изменить объект |
add_post | Вызывается после того как в базу запись добавлена. Здесь можно производить отправление уведомлений на почту |
edit | Вызывается при вызове showEdit |
edit_pre | Вызывается перед тем как в базе изменить запись. Здесь можно предварительно изменить объект |
edit_post | Вызывается после того как запись уже изменена. Здесь можно производить отправление уведомлений на почту |
delete | Вызывается при вызове showDelete |
delete_pre | Вызывается когда из базы удаляется объект, либо ему ставиться isDeleted |
delete_post | Вызывается когда когда из базы уже удален объект, либо ему уже стоит isDeleted |
restore | Вызывается при вызове showRestore |
restore_pre | Вызывается когда ставиться isDeleted=0 |
restore_post | Вызывается когда уже поставлен isDeleted=0 |
searchFilter | Вызывается, когда происходит фильтрация данных через функции getData, niceData. |
getObjectFilter | Вызывается, когда происходит редактирование, удаление, вобщем любой вызов$c = call_user_func_array([get_called_class(), 'getObject'], [$params]);Приводит к вызову этой функции и получению фильтра |
searchOrder | Вызывается, когда происходит сортировка данных через функции getData, niceData. |
add_pre_after_process, edit_pre_after_process | Данные функции вызываются после add_pre, но после того как данные отформатированы и файлы залиты в систему, но в базу еще объект не был сохранен |
Пример search_post
if($event == 'search_post')
{
parent::event($event, $params);
$data = xarr($ioData, 'data');
$data2 = xarr($ioData, 'data2');
$users_pkid = [];
foreach($data2 as $i => $row)
{
$user_pkid = xarr($row, 'user_pkid');
$judge_user_pkid = xarr($row, 'judge_user_pkid');
$users_pkid[$user_pkid] = $user_pkid;
$users_pkid[$judge_user_pkid] = $judge_user_pkid;
}
$g_users = IOCore::call(
'com.class.user.findByID',
[
'id' => $users_pkid,
]
);
foreach($data2 as $i => $row)
{
$user_pkid = xarr($row, 'user_pkid');
$judge_user_pkid = xarr($row, 'judge_user_pkid');
$user = xarr($g_users, $user_pkid);
$judge = xarr($g_users, $judge_user_pkid);
if($user != null)
{
$ioData['data'][$i]['user_pkid'] = xarr($user, 'sname') .' '. xarr($user, 'fname') .' '. xarr($user, 'lname') . ' /'. xarr($user, 'login');
}
if($judge != null)
{
$ioData['data'][$i]['judge_user_pkid'] = xarr($judge, 'sname') .' '. xarr($judge, 'fname') .' '. xarr($judge, 'lname') . ' /'. xarr($judge, 'login');
}
if(xarr($row, 'isCheck', false))
{
$ioData['data'][$i]['buttons']['accept']['value'] = 0;
$ioData['data'][$i]['buttons']['delete']['value'] = 0;
$ioData['data'][$i]['buttons']['restore']['value'] = 0;
$ioData['data'][$i]['buttons2']['delete']['value'] = 0;
$ioData['data'][$i]['buttons2']['restore']['value'] = 0;
}
}
return 1;
}
Обратите внимание на порядок вызова. Сначала вызывается parent::event($event, $params), а в конце обязательно return 1.
Пример init
ВАЖНО!
Функция init ОЧЕНЬ ВАЖНА для модели, так как в ней делаются все проверки прав доступа. Она должна вернуть либо EIO_OK, либо EIO_NO_PERMISSIONS.
Пример функции:
if($event == 'init')
{
if($action == 'actionReg')
{
if(!$ioSession->user->isGuest())
{
return EIO_OK;
}
}
if(site_permission2($site_pkid))
{
return EIO_OK;
}
return EIO_NO_PERMISSIONS;
}
Функцию нужно писать следующим образом:
- Сначала проверить что разрешено и вернуть
EIO_OK. - Для всего что не разрешено, запретить, вызвав
EIO_NO_PERMISSIONS.
Пример фильтров
Эти функции нужны, чтобы пользователи не могли видеть данные друг друга. Обычно этот фильтр ограничаевает данные по site_pkid либо company_pkid.
if($event == 'searchFilter')
{
$params['xfilter'] = [
'site_pkid' => $site_pkid,
//'isDeleted' => false,
];
$params['defaultFilter'] = [
'isDeleted' => false,
];
$filter = parent::event($event, $params);
return $filter;
}
if($event == 'getObjectFilter')
{
$params['xfilter'] = [
'site_pkid' => $site_pkid,
//'isDeleted' => false,
];
$filter = parent::event($event, $params);
return $filter;
}
$params['xfilter']означает, что фильтр будет добавлен по любому и не может быть переопределен при вызовеcallAction.$params['defaultFilter']означает, что фильтр будет добавлен, если не определен при вызовеcallActionэтот параметр, в противном случае возмется переданный параметр.
Пользовательский метод
Ниже приведен код простой пользовательского метода doAbc => actionAbc, который возвращает массив переданных параметров ret_params, переменную a и error_code = операция выполнена (EIO_OK).
/**
* Пример пользовательской функции
*/
public static function doAbc($result, $params)
{
$result->ret_params = $params;
$result->a = xarr($params, 'a', 456);
// логика...
$result->error_code = EIO_OK;
__exit;
return $result;
}
Вызов model метода
В controller и model
$ret = IOCore::callAction(
'app.model.example.abc',
[
'a' => 123,
]
);
if(xarr($ret, 'error_code', EIO_FALSE) == EIO_OK)
{
$ret_params = xarr($ret, 'ret_params', []);
$recipientEmail = xarr($ret, 'recipient_email');
$a = xarr($ret, 'a');
v_dump($ret_params);
v_dump($recipientEmail);
v_dump($a);
}
else
{
echo 'error ' . xarr($ret, 'error_code', EIO_FALSE) . ': ' . xarr($ret, 'error_str', '');
}
v_dump($ret);
В Twig
{% set r = ioCallAction(
'app.model.example.abc',
{
'a' => 123,
}
) %}
{{v_dump(r)}}
Через URL
Нужно прописать в браузере путь до нашей модели по типуhttps://site.kz/entity/app.model.example.abc
где .abc это пользовательский метод
Чтобы получить результат в формате JSON, нужно добавить GET параметр &resultType=json в конце URL.

Работа с WidgetModel4
Данный раздел описывает методы работы с BMC CMS и написание виджетов. Данный раздел актуален для виджетов, написанных на основе класса WidgetModel4.
Работа с виджетами происходит из шаблонов Twig вызовами методов: widgetRender, widgetData, widgetRenderByType, widgetDataByType.
| Название | Описание |
|---|---|
widgetRender($widget_name, $default) | Вызывает рендер виджета.
|
widgetData($widget_name, $default) | Получает значение данных виджета, без вызова рендера.
|
widgetRenderByType($widget_type, $widget_params, $default) | Вызывает рендер виджета, используя тип и параметры виджета:
|
widgetDataByType($widget_type, $widget_params, $default) | Получает значение данных виджета, без вызова рендера:
|
Типы виджетов:
label– текстовое поле.menu- меню.
Параметры виджетов, переменная $widget_params:
widget_name.foreignPkid.widget_uq, обычно берется изsettings.xml, но если виджет там не объявлен, то можно установитьwidget_uqздесь.
Если widget_name объявлен в settings.xml и он не совпадает с $widget_type, который вы указали в методе, то будет выдана ошибка о не соотвествии типов. Методы widgetRenderByType() и widgetDataByType(), обычно используются, если виджеты не были объявлены в settings.xml.
Методы виджета:
doRender().doSettings().doData().