Иерархическая модель объекта на MongoDB. Версия 3.0
Компонент добавляет на сайт компонент иерархическую модель атрибутов, которая позволяет реализовать широкий вид функцианала, вроде каталога товаров, раздела статей и новостей и т.д.
Для работы компонента на ваш сервер должен быть установлен MongoDB. Установка под различные системы хорошо описана на официальном сайте:
http://docs.mongodb.org/manual/tutorial/install-mongodb-on-windows/
Если вы разрабатываете ваш сайт под Windows, то для тестирования на локальной машине рекомендуем вам установить Open Server.
Он сам устанавливает MongoDB на локальную машину и так же ставит полезный инструмент rockmongo
(аналог phpmyadmin для MySQL).
Заметим что Denwer на текущий момент (июнь 2015 года) такими возможностями не обладает.
Возможности компонента
Всё что вам нужно для работы с компонентом - это создать модель, унаследованную от класса NodeMongoModel и в её конструкторе указать список полей, которые должны быть у объекта, и название коллекции (таблицы).
Пример:
class ItemNodeModel extends NodeMongoModel { public function __construct($id = NULL, $onlyShow = false, $lang = LANG) { parent::__construct("items", $id, $onlyShow, $lang); // Базовый констуктор всегда вызываем самым первым и прописываем в нём имя таблицы. Этот вызов идёт первым. $this->NInit('code', NT_STR, 'Артикул'); $this->NInitMultiLang('name', NT_STR, 'Название'); $this->NInitMultiLang('text', NT_TEXT, 'Описание'); $this->NInit('price_rub', NT_FLOAT, 'Цена рублей'); $this->NInit('photo', NT_IMG, 'Фотография'); $this->NSetClassTitle('Товар'); // Не обязательно, но желательно для админки } }
Теперь в скриптах сайта вы можете работать с объектами этого типа:
$item = new ItemNodeModel(); $item->code = "АЛ109941"; $item->price_rub = 1999.95; $item->photo = "photo1.jpg"; // Фото должно находиться в папке указанной в $g_config['node_model']['upl_file_path'] $item_id = $item->Flush(); // Сохранить объект
После этого новый элемент был добавлен в базу. Зная id элемента можно создать его экземпляр:
$item = new ItemNodeModel($item_id);
Этот же id можно получить обратившись к полю _id:
echo $item->_id; // Вернёт что-то наподобие "507f191e810c19729de860ea"
Подробнее об _id в MongoDB и о том как он генерируется можно почитать в официальной документации:
http://docs.mongodb.org/manual/reference/object-id/
Созданые объекты можно изменять:
$item = new ItemNodeModel($item_id); $item->price_rub = 2499.95; $item->Flush(); // Записать изменения
Редактировать на разных языках:
$item = new ItemNodeModel($item_id); $item->NSetCurLang('ru'); // Код языка это ключ из $g_arrLangs $item->name = "Игрушка"; $item->NSetCurLang('en'); $item->name = "Toy"; $item->Flush(); // Записать изменения
Поиск реализован через основной метод NFind() и дополнительные NFindEx(), NFindAll(), NFindOne(). Получить все существующие объекты:
$model = new ItemNodeModel(); $total = 0; $ids = $model->NFind($total); // Здесь получаем id всех объектов.
Теперь имя id элементов можно создать и сами объекты:
$all = array(); foreach($ids as $id) { $all[] = new ItemNodeModel($id); }
Более сложный случай:
$filter = array(); $filter["price_rub"] = 1999.95; // Будем искать все объекты у которых цена равна 1999.95 $skip = 20; // Начиная с какого элемента $count = 10; // Сколько элементов извлечь $model = new ItemNodeModel(); $total = 0; // Сюда будет записано общее количество подходящих элементов $ids = $model->Find($total, $filter, $skip, $count, "price_rub"); // Здесь получаем id найденых элементов. Полученные элементы будут упорядочены по полю price_rub.
С найдеными элементами можно выполнять любые операции, в том числе вывести в шаблоне:
-
<?php foreach ($all as $a):?>
- <?= "ID объекта: {$a->_id}, Артикул: {$a->code}, Цена: {$a->price_rub}"?> <?php endforeach?>
Так же существует компонент, который позволяет добавлять и редактировать объекты через административный раздел.
Несколько значений у одного атрибута
Каждый атрибут может иметь ни только одно значение, а намного больше. По сути значение всегда является массивом значений. Просто при использовании привычной записи $a->some_field, мы просто всегда обращаемся к первому элементу массива.
Записать более одного значения атрибута можно используя следующий синтаксис:
$item->category(0, "Куртка"); // Аналог: $item->category = "Куртка"; $item->category(1, "Жакет"); $item->category(2, "Ветровка");
Чтение тогда будет выглядеть следующим образом:
echo $item->category(0); // Выведет "Куртка". Аналог: echo $item->category; echo "
"; echo $item->category(1); // Выведет "Жакет". echo "
"; echo $item->category(2); // Выведет "Ветровка".
Массив всех значений аттрибута можно получить, используя следующий синтаксис:
echo implode(", ", $item->category()); // Выведет "Куртка, Жакет, Ветровка"
Ещё один вариант вывести список значений поля:
-
<?php for ($i = 0; $i < count($item->category()); ++$i):?>
- <?= $item->category($i)?> <?php endfor?>
Инициализация атрибутов
Для инициализации атрибутов существуют следующие основные методы:
- NInit - простая инициализация атрибута;
- NInitMultiLang - простая инициализация мультиязычного атрибута;
- NInitEnum - задание перечисления для атрибута с типом NT_ENUM;
- NCount - задание количества возможных значений атрибута в админке.
Так же есть дополнительные методы:
- NInitVirt - инициализация виртуального атрибута, после чего он станет доступен для редактирования в админке. В остальных случаях виртуальный аттрибут инициализировать не обязательно;
- NInitObj - задание класса для атрибута с типом NT_OBJ. Класс должен быть унаследован от NodeMongoModelBase. Подробнее об этом функционале будет рассказано ниже.
- NSetTitle - задание или изменение заголовка атрибута. Бывает необходимо, когда мы не хотим инициализировать виртуальный атрибут, но хотим чтобы у него был заголовок, например для отображения соотв. столбца в списке в админке.
Простая инициализация атрибута NInit() и NInitMultiLang()
Как вы заметили базовая инилицализация полей объекта осуществляется через методы NInit и NInitMultiLang. Каждый принимает 3 параметра:
- - Обозначение атрибута, по которму мы к нему обращаемся. Можно использовать маленькие латинские символы, нижнее подчеркивание и цифры.
- - Тип атрибута (доступные будут перечислены далее)
- - Заголовок атрибута. Используется в админке и методе NGetTitle(). Для многоязычных сайтов их можно вынести в lang файл (в папку autoload).
Инициализация допустимого количества значений NCount()
В административном разеде по умочанию считается что у атрибута всегда редкатируется только одно значение. Но используя метод NCount() при инициализации, можно это исправить и указать до сколько значений вы хотели бы иметь возможность вписать в аттрибут.
class ItemNodeModel extends NodeMongoModel { public function __construct($id = NULL, $onlyShow = false, $lang = LANG) { parent::__construct("items", $id, $onlyShow, $lang); // Базовый констуктор всегда вызываем самым первым $this->NInit('customer', NT_ENUM, 'Покупатель'); $this->NCount('customer', 3); // Число покупателей может быть до трёх } };
То есть теперь в админке мы можем добавлять дополнительные значения для атрибута, но не больше чем заданное число.
Не ставьте это число слишком большим - в силу технических причин это может привести к замедлению работы административного раздела во время редактирования/добавления объекта.
Инициализация допустимого значений у перечисления NInitEnum()
Если аттрибут имеет тип NT_ENUM (перечисление), то при инициализации с помощью NInitEnum() нужно указать допустимый список значений.
Подрбнее это описано в описании типа NT_ENUM.
Типы атрибутов
В компоненте существуют следующие типы атрибутов:
- NT_OBJ - контейнер. Может содержать другие под-переменные. В дальнейшем будет показан пример использования;
- NT_MONGOID - ID другово объекта. Аналог FOREIGN KEY в MySQL;
- NT_STR - строка. Имеет поддежку многоязычности;
- NT_TEXT - аналогичен NT_STR, отличается только использованием Rich Text полем редактирования в административном разделе;
- NT_TEXTAREA - аналогичен NT_STR, отличается только использованием textarea полем редактирования в административном разделе;
- NT_FLOAT - число с плавающей точкой;
- NT_INT - целое число;
- NT_IMG - имя файла изображения. В административном разделе для этого типа можно загружать фотографии;
- NT_FILE - имя файла. В административном разделе для этого типа можно загружать файлы;
- NT_ENUM - перечисление. Работа с этим типом будет описана далее;
- NT_BOOL - булеан;
- NT_UDATE - дата хранимая в формате unixtime (по сути целое число).
- NT_DATE - дата хранимая в виде строки в формате "Y-m-d". По сути NT_STR, отличается только полем редактирования в админке;
- NT_TIME - время хранимое в виде строки в формате "{часы}:{минуты}", например "22:15". По сути NT_STR, отличается только полем редактирования в админке;
- NT_COLOR - цвет хранимый в виде строки в шестнадцатиричном формате, например "#1144BB". По сути NT_STR, отличается только полем редактирования в админке;
- NT_AUTOINC - автоматически увеличивающийся целочисленный счётчик. Аналог AUTO_INCREMENT поля в MySQL;
- NT_SPECIAL - специальный атрибут для создания своего особого поля ввода;
- NT_UCALENDAR - специальный дополнительный тип в виде календаря с отмечаемыми датами;
Базовые типы NT_STR, NT_TEXT, NT_TEXTAREA, NT_INT, NT_FLOAT, NT_DATE, NT_TIME, NT_COLOR
Работа с данными типами уже описана выше. Полученное значение всегда аналогично присвоенному, даже при использовании метода NRender().
Тип NT_OBJ
Тип позволяет создавать у объектов вложенную/иерархическую структуру. Пример инициализации данного типа:
class EventNodeModel extends NodeMongoModel { public function InitEventBasics() { $this->NInit('photo', NT_OBJ, 'Фотография'); $this->NInit('photo.file', NT_IMG, 'Фото'); $this->NInit('photo.title', NT_STR, 'Подпись'); $this->NCount('photo', 24); } }
Пример использвания на сайте:
$event = new EventNodeModel(); $event->photo->file = "1.png"; // Можно записать так: $event->photo(0)->file $event->photo->title = "First photo"; // Можно записать так: $event->photo(0)->title $event->photo(1)->file = "2.png"; $event->photo(1)->title = "Second photo"; echo $event->photo(1)->file; // Выведет "2.png" echo $event->photo(1)->title; // Выведет "Second photo" echo NodeImgUrl($event->photo(1)->file, 200, 100, "fitout"); // Выведет ссылку на провью изображение 200x100
Указывать NT_OBJ является хорошим тоном, но совершенно не обязательно. Такой код тоже будет работать:
class EventNodeModel extends NodeMongoModel { public function InitEventBasics() { $this->NInit('photo.file', NT_IMG, 'Фото'); $this->NInit('photo.title', NT_STR, 'Подпись'); $this->NCount('photo', 24); } }
Тип NT_IMG
Пример использвания на сайте:
$gallery = new GalleryNodeModel(); $gallery->photo = "photo.png"; // Файл "photo.png" должен находится в $g_config['node_model']['upl_file_path'] echo $gallery->photo; // Выведет "photo.png" echo NodeImgUrl($gallery->photo, 200, 100); // Выведет ссылку на провью изображение 200x100 echo NodeImgUrl($gallery->photo, 200, 100, "scale"); // Выведет ссылку на прeвью изображение 200x100 echo NodeImgUrl($gallery->photo, 200, 100, "fitin"); // Выведет ссылку на прeвью изображение 200x100 с дорисовкой фона до точного разрешения echo NodeImgUrl($gallery->photo, 200, 100, "fitout"); // Выведет ссылку на прeвью изображение 200x100 с обрезкой картинки до точного разрешения
Для получения ссылки на изображение, используется функция NodeImgUrl(). Перым параметом у неё идёт имя файла.
Вторым и третьим параметом она принимает ширину и высоту до которой нужно уменьшить изображения, а способ уменьшения.
Если указывать только первый параметр, то метод вернёт ссылку на оригинальное изображение.
Подробнее о значениях scale, fitin и fitout в четвертом параметре функции NodeImgUrl() можно почитать в описании компонента Получение обработанного изображения
Тип NT_FILE
Пример использвания на сайте:
$item = new ItemNodeModel(); $item->file = "file.doc"; // Файл "photo.png" должен находится в $g_config['node_model']['upl_file_path'] echo $item->file; // Выведет "photo.png" echo NodeFileUrl($item->file); // Выведет ссылку на файл
Для получения ссылки на файл, используется функция NodeFileUrl(). Перым параметом у неё идёт имя файла.
Для скачивания файлов используется компонент BrowserDataCache, поэтому по умолчанию разрешено скачивать только некоторые типы файлов (это можно изменить настроив компонент).
Тип перечисление NT_ENUM
Пример инициализации данного типа:
define("SEX_NONE", 1); define("SEX_MALE", 2); define("SEX_FEMALE", 3); class UserNodeModel extends NodeMongoModel { const REMEMBER_PERIOD = 604800; // Время хранения авторизации (в секундах) public function __construct($id = NULL, $onlyShow = false, $lang = LANG) { parent::__construct("users", $id, $onlyShow, $lang); $this->NInit('sex', NT_ENUM, 'Пол'); // Декларация // Возможные варианты $this->NInitEnum('sex', SEX_NONE, 'Не указан'); $this->NInitEnum('sex', SEX_MALE, 'Мужской'); $this->NInitEnum('sex', SEX_FEMALE, 'Женский'); } };
Пример использвания на сайте:
$user = new UserNodeModel(); $user->sex = SEX_FEMALE; echo $user->sex; // Выведет "3" echo $user->NRender("sex"); // Выведет "Женский"
При этом значения перечислений могут быть не только целыми числами, допустимы и обычные строки:
define("SEX_NONE", "none"); define("SEX_MALE", "male"); define("SEX_FEMALE", "female");
Тип NT_BOOL
Пример использвания на сайте:
$item = new ItemNodeModel(); $item->is_show = true; echo $item->is_show; // Вернет 1 echo $user->NRender("is_show"); // Выведет 'Да' или 'Yes' $item->is_show = false; echo $item->is_show; // Вернет 0 echo $item->NRender("is_show"); // Выведет 'Нет' или 'No'
Тип NT_UDATE
Пример использвания на сайте:
$item = new ItemNodeModel(); $item->date = 1403896653; echo $item->date; // Выведет 1403896653 echo $item->NRender("date"); // Выведет '27.06.2014'
Тип NT_AUTOINC
Автоматически увеличивающийся целочисленный счётчик. Аналог AUTO_INCREMENT поля в MySQL.
В работе атрибут аналогичен типу NT_INT. Но отличается тем, что сам автоматически записывается при создании объекта (т.е. при первом сохранении), хотя можно задать и своё значние вручную.
Алгоритм таков, что запишется макс. значение, найденное среди все объектов, увеличенное на единицу. Если создаётся первый объект то запишется 1.
!!! Работает пока только для корневых полей.
Тип NT_SPECIAL
Специальный тип для инициализации виртуальных атрибутов, который позволяет делать своё поле отображения/сохранения значения в админке. Для работы нужно переопределять метод NRenderSpecialInput(). Пример использования:
class CityNodeModel extends NodeMongoModel { public function __construct($id = NULL, $onlyShow = false, $lang = LANG) { // ... $this->NInitVirt('set_region', NT_SPECIAL, 'Регион к которому принадлежит'); // После NInitVirt() аттрибут будет доступен в админке $this->NInit('_region_id', NT_MONGOID, 'Регион к которому принадлежит'); // ... } //... protected function NGetVirt($name, $index) { if ($name == "set_region") { return $this->_region_id; } return parent::NGetVirt($name, $index); } protected function NSetVirt($name, $index, $value) { if ($name == "set_region") { return $this->_region_id = $value; } return parent::NSetVirt($name, $index, $value); } public function NRenderSpecialInput($name, $data) { if ($name == "set_region") { ob_start(); IncludeCom("admin/my_node_input_regions", $data); return ob_get_clean(); } return parent::RenderSpecialInput($name, $data); } } //** Содержимое файла "/tpl/admin/my_node_input_regions.php":
Поиск элементов через NFind()
Метод NFind() предоставляет широкие возможности по поиску нужных объектов в базе. Метод имеет следующие аргументы:
Аргумент $total - это ссылка на переменную, в которую будет записано общее число объектов, удовлетворяющее аргументу $filter.
Аргумент $filter позволяет задавать атрибуты, по которым нужно произвести поиск.
Ключ массива обозначает название атрибута, по которому нужно провести поиск, а значение - параметр по которому это поле фильтруется.
Причём значение может быть не прямым значением атрибута в объекте, а содержать в себе диапазон, массив допустимых значений и т.д.
Возможности поиска представлены в следующем примере:
$filter = array(); $filter["some_val"] = 'Ololo'; // Все элементы у которых параметр 'some_val' равен "Ololo". $filter["some_int"] = array('min' => 3007); // Все элементы у которых числовой параметр 'some_int' больше 3007. $filter["some_int"] = array('min' => 3007, 'max' => 4020); // Все элементы у которых числовой параметр 'some_int' больше 3007 и меньше 4020. $filter["some_int"] = array('emin' => 3007, 'emax' => 4020); // Все элементы у которых числовой параметр 'some_int' больше-равно 3007 и меньше-равно 4020. $filter["some_val"] = array('a1', 'a2', 'a3'); // Все элементы у которых 'some_val' равно 'a1' или 'a2' или 'a3'. $filter["text1,text2"] = "ABC"; // Все элементы у которых либо параметр 'text1' либо параметр 'text2' равен 'ABC'. $filter["some_val"] = array('like' => "text"); // Все элементы у которых параметр 'some_val' содержит подстроку "text". Не чувствителен к регистру. $filter["some_val"] = array('not' => "text"); // Все элементы у которых параметр 'some_val' не равен "text". $filter["some_val"] = array('exists' => true); // Все элементы у которых параметр 'some_val' существует (если false то не существует). // Допустимы и вложенные атрибуты: $filter["preview.file"] = array('like' => ".png"); // У поля preview подполе file должна быть подстрока содержащая ".png"
ВАЖНО!!! Помните что атрибут дожен быть реальным полем. Виртуальные атрибуты не работают.
Аргумент $skip - сколько элементов пропустить с начала списка.
Аргумент $count - сколько элементов должно быть в списке.
Аргумент $orderBy - название поля, по которому нужно упорядочить элементы. Примеры принимаемых значений:
- "key1" - сортировать по атрибуту key1;
- array("key1" => 1) - то-же самое;
- array("key1" => -1) - сортировать по атрибуту key1 в обратном порядке;
- array("key1", "key2") - сортировать по атрибутам key1 и key2;
- array("key1" => 1, "key2" => -1) - сортировать по аттрибуту key1 в прямом порядке и аттрибуту key2 в обратном порядке;
- NM_SORT_RANDOM - сортировать в случайном порядке.
!!! ВАЖНО: Сортировка по двум атрибутам сразу в данный момент не работает.
Аргумент $lang - применим только строковых многоязычных параметров. Поиск будет произовдиться только в пределах данного языка. По умолчанию равен текущему языку. Можно задать массив языков, например array("en", "ru").
Дополнительные методы поиска
-
NFindDistinct($key, $filter = array(), $lang = LANG) -
возвращает все возможные значения атрибута у элементов, которые соответствуют фильтру.
Первый параметр $key указывает аттрибут, для которого нужно вернуть все существующие значения.
Параметр $filter аналогичен такому же аргументу в NFindOne().
Если атрибут $key многоязычный, то третий аргумент $lang определяет язык в пределах которого производить поиск (можно искать только на одном языке) -
NFindEx(&$hasNext, $filter = array(), $skip = 0, $count = 0xffff, $order = NULL, $lang = LANG) -
метод полностью аналогичен NFind() за исключением первого аргумента $hasNext.
Здесь в него по ссылке запишется true, если есть ещё элементы далее за пределами извлекаемого диапазона.
Данный метод является более оптимальным с точки зрения производительности. -
NFindAll($filter = array(), $order = NULL, $getObjects = true, $lang = LANG) -
найти и вернуть все элементы соответствующий фильтру.
Параметры $filter и $order аналогичны такому же аргументу в NFindOne().
Если третий $getObjects параметр равен false то будет возвращён массив id, а если true, то будет создан массив объектов соответствующих этим id.
Четвертый аргумент $lang определяет текущий язык для возвращаемых объектов. -
NFindOne($filter, $getObject = true, $lang = LANG) -
найти и вернуть первый элемент соответствующий фильтру.
Параметр $filter аналогичен такому же аргументу в NFindOne().
Если второй аргумент $getObjects равен false то будет возвращён id, а если true, то будет создан объект соответствующий id.
Если второй аргумент $getObjects равен true, а ни один элемент не найден, то будет возвращён пустой объект.
Третий аргумент $lang определяет текущий язык для возвращаемого объекта.
Форматирование значение атрибутов с помощью NRender()
Любой атрибут можно получить в более удобном для восприятия отформатированном виде. Особенно это актуально для типов: NT_ENUM, NT_BOOL, NT_UDATE. Для получения отформатированного значения используется метод NRender(). Он принимает название атрибута, индекс и разделитель. Если индекс не задан и в аттрибуте находится больше одного значения, то будут возвращены все значения, через разделитель. Примеры:
$user->sex = SEX_FEMALE; echo $user->sex; // Выведет 2 echo $user->NRender("sex"); // Выведет "Женский" echo $user->NRender("sex", 0); // Выведет "Женский" $user->sex(0, SEX_FEMALE); $user->sex(1, SEX_MALE); echo $user->NRender("sex", 0); // Выведет "Женский" echo $user->NRender("sex", 1); // Выведет "Мужской" echo $user->NRender("sex"); // Выведет "Женский, Мужской" echo $user->NRender("sex", NULL, "|"); // Выведет "Женский|Мужской" //** $user->is_valid = false; echo $user->is_valid; // Выведет 0 echo $user->is_valid("sex"); // Выведет "Нет" $user->is_valid = true; echo $user->is_valid; // Выведет 1 echo $user->is_valid("sex"); // Выведет "Да" //** $item->date = 1403896653; echo $item->date; // Выведет 1403896653 echo $item->NRender("date"); // Выведет '27.06.2014' $item->date(0, 1403896653 + 0 * 24 * 60 * 60); $item->date(1, 1403896653 + 1 * 24 * 60 * 60); $item->date(2, 1403896653 + 2 * 24 * 60 * 60); echo $item->NRender("date", 1); // Выведет '28.06.2014' echo $item->NRender("date"); // Выведет '27.06.2014, 28.06.2014, 29.06.2014'
Важно отметить ещё одну важную особенность метода NRender() касающуюся многоязычных полей. Если не удалось получить не пустое значение аттрибута на текущем языке объекта, то будет попытка получить такое же значение на языке по умолчанию. Пример:
// Пускай DEF_LANG равен 'en'. Тогда: $user->NSetCurLang('en'); $user->name = "Item"; echo $user->name; // Выведет "Item" echo $user->NRender("name"); // Выведет "Item" $user->NSetCurLang('ru'); echo $user->name; // Выведет "", так как значение на русском не задано. echo $user->NRender("name"); // Выведет "Item". Хоть значение на русском у нас и не задано, но NRender() возьмёт значение на языке по умолчанию. $user->name = "Изделие"; echo $user->name; // Выведет "Изделие" echo $user->NRender("name"); // Выведет "Изделие"
Виртуальные атрибуты
Виртуальные атрибуты, это не сущетвующие реально атрибуты, но тем не менее доступные для установки и запросе значений.
Действие выполняемое при запросе или установке атрибута определяется в методах NGetVirt() и NSetVirt().
Первый параметр в обоих методах это название атрибута, а третий в NSetVirt() это присваиваемое значение.
Пример:
class UserNodeModel extends NodeMongoModel { // ... protected function NGetVirt($name, $index) { if ($name == "full_name") { return $this->first_name . " " . $this->last_name; } return parent::NGetVirt($name, $index); } protected function NSetVirt($name, $index, $value) { if ($name == "full_name") { $arr = explode(" ", $value); $this->first_name = $arr[0]; $this->last_name = $arr[1]; return; } return parent::NSetVirt($name, $index, $value); } } // Использование: $user->full_name = "Владимир Ильич"; echo $user->full_name; // Выведет "Владимир Ильич"
Второй параметр в обоих методах нужен для того случая, когда виртуальный аттрибут представляет собой массив значений. Параметр представляет собой индекс в массиве.
Но в этом случае желательно переопределить метод NGetVirtCount(), иначе метод NRender() для такого виртуального атрибута может работать некорректно.
Пример:
class UserNodeModel extends NodeMongoModel { $private $inner = array(); // ... protected function NGetVirt($name, $index) { if ($name == "arr") { return @$this->inner[$index]; } return parent::NGetVirt($name, $index); } protected function NSetVirt($name, $index, $value) { if ($name == "arr") { return $this->inner[$index] = $value; } return parent::NSetVirt($name, $index, $value); } protected function NGetVirtCount($name) { if ($name == "arr") { return count($this->inner); } return parent::NGetVirtCount($name); } } // Использование: $user->arr = "A"; $user->arr(1, "B"); $user->arr(4, "C"); print_r($user->arr()); // Выведет ["A", "B", "C"] echo $user->NRender("arr"); // Выведет "A, B, C"
Так же есть дополнительный метод NIsVirt() позволяющий узнать является ли аттрибут виртуальным или нет:
echo $user->NIsVirt("arr"); echo $user->NIsVirt("map.image"); echo $user->map->NIsVirt("image");
!!! ВАЖНО: Методы NGetVirt(), NSetVirt(), NGetVirtCount() работают только для корневых атрибутов.
Если вам нужно определить дейтствия для вложенных атрибутов, то следует унаследоваться от NodeMongoModelBase и зарегестрировать класс методом NInitObj().
Более подробно это описано в соответствующем разделе.
Виртуальные атрибуты в админке
Виртуальный атрибут можно зарегистрировать в конструктуре с помощью метода NInitVirt().
После этого виртуальный атрибут станет доступен для редактирования в админке, как другие обычные атрибуты.
Аргументы метода аналогичны методу NInit(). Второй параметр (тип) в данном случае определяет то, в каком поле ввода будет редактироваться значение.
class ItemNodeModel extends NodeMongoModel { public function __construct($attr_id = NULL, $onlyShow = false, $lang = LANG) { // ... $this->NInitVirt("category", NT_STR, "Категория"); } } // ...
Назнечение действий на события сохранения и валидации объекта
У класса можно перегрузить следующие функции:
- NOnValidate() - вызывается только во время сохранения объекта в админке и возвращает массив ошибок, если данные объекта некорректны;
- NOnFirstSave() - вызывается при первом сохранении объекта (в момент когда он ещё не существует, но имеет изменённые данные, а значит будет записан);
- NOnSave() - вызвается при каждом сохранении изменённого обхекта.
Пример:
class ItemNodeModel extends NodeMongoModel { // ... public function NOnValidate() { $errs = parent::NOnValidate(); // Можно и просто array() if ($this->name == "") { $errs[] = "Пустое имя"; } return $errs; } public function NOnFirstSave() { $this->_added = time(); } public function NOnSave() { $this->_modified = time(); } } // ...
Смена языка на ходу
Текущий язык объекта можно завдать в третьем параметре конструктора (по умолчанию текущий язык).
Но так же язык можно менять прямо на ходу с помощью метода NSetCurLang():
$user->NSetCurLang('ru');
Получить текущий язык можно с помощью метода NGetCurLang():
echo $user->NGetCurLang(); // Вернёт 'ru' или другой доступный язык
Задание класса для атрибута с типом NT_OBJ через NInitObj()
Фактически необходимо лишь во вложенных узлах для обработки виртуальных атрибутов и для переопределения NRender().
@todo Пример.
Дополнительные методы
У класса можно перегрузить следующие функции:
- NGetTitle($name) - получить заголовок атрибута;
- NSetTitle($name, $title) - установить заголовок атрибута;
- NGetMaxCount($name) - возвращает значение присвоенное с помощью NCount(), либо 1;
- NGetType($name) - получить тип атрибута;
- NGetEnums($name) - полуить массив допустимых перечислений атрибута (массив "Ключ" => "Заголовок");
- NIsMultiLang($name) - возвращает true если атрибут мультиязычен;
- NGetVisibleNodeNames() - возвращает массив публичных аттрибутов (т.е. которые можно редактировать у объекта в админке);
- NClearAll() - ощистить объект от всех значений;
- NClearNode($name) - полностью очистить конкретное поле (удобно для массивов - не нужно присваивать пустую строку каждому полю).
Стандартные методы модели
Присутствуют и в других видах моделей и имеют тот же смысл:
- IsDeleted() - возвращает true, если объект был удалён;
- IsExists() - возвращает true, если объект уже существует в базе;
- HasChanges() - возвращает true, если атрибутам объекта присваивались значения (даже виртуальные);
- IsOnlyShow() - возвращает true, если объект запрещено редактировать.
Работа с админкой
Создать административный раздел для редактирование объектов какой либо модели довольно просто.
Всё что нужно - это создать файл раздела, например /src/admin/items.php и вызвать в нём IncludeCom("admin/node_model/list", array(/* Параметры */)).
Данный вызов создаст на этой странице блок поиска (фильтрации) и таблицу со списком объектов.
В данном примере показано какие параметры нужно передать в компонент:
IncludeCom("admin/node_model/list", array ( // Атрибуты, которые будут являться столбцами в таблице объектов (на данный момент поддерживаются не все типы) "table" => array("name", "desc", "photo", "price_rub", "is_show", "_added"), // Имя нашего класса "class" => "ItemNodeModel", // Атрибуты, по которым можно будет производить поиск/фильтрацию. Будут отображаться в специальном блоке над таблицей. // Если параметр не указан, то будет равен значению указанному в "table". "search" => array("name", "inner_code"), // Поле, по которому сортировать элементы по умолчанию. Не обязательный параметр. "order" => "_added", // Прямой (1) или обратный (-1) порядок сортировки по умолчанию. Не обязательный параметр. "order_dir" => -1, // Базовый фильтр, по которому будут отобраны объекты. Аналогичен параметру filter в NFind(). // При поиске/фильтрации по параметрами указанным в "search", эти параметры будут накладываться на базовый фильтр. // Не обязательный параметр, по умолчанию array(). "filter" => array("sex" => array(SEX_MALE, SEX_FEMALE), "is_show" => true), ));
Важно не забыть добавить раздел в главное меню админки. Достаточно в файле конфигурации (папка /core/config/) указать что-то вроде:
<?php require_once BASEPATH . 'core/config/admin_menu.php'; GetQuery(); // Чтобы фунция SiteRoot корректно заработала нужно проинициализировать LANG в функции GetQuery() $g_config['admin_menu'][] = array ( 'link' => SiteRoot('admin/items'), 'name' => 'Изделия', // Название пункта меню 'label' => '', 'css' => '', 'list' => array() ); ?>