Иерархическая модель объекта на MongoDB

Компонент добавляет на сайт компонент иерархическую модель объекта, которая позволяет реализовать широкий вид функцианала, вроде каталога товаров, раздела статей и новостей и т.д.

Для работы компонента на ваш сервер должен быть установлен MongoDB. Установка под различные системы хорошо описана на официальном сайте:
http://docs.mongodb.org/manual/tutorial/install-mongodb-on-windows/
Если вы разрабатываете ваш сайт под Windows, то для тестирования на локальной машине рекомендуем вам установить Open Server. Он сам устанавливает MongoDB на локальную машину и так же ставит полезный инструмент rockmongo (аналог phpmyadmin для MySQL).
Заметим что Denwer на текущий момент такими возможностями не обладает.

Возможности компонента

Всё что вам нужно для работы с компонентом - это создать модель, унаследованную от класса AttrComblexModel и указать в её конструкторе список полей, который должны быть у объекта.
Пример:


    class ItemNodeModel extends NodeMongoModel
    {
        public function __construct($id = NULL, $onlyShow = false, $lang = LANG)
        {
            parent::__construct("items", $id, $onlyShow, $lang); // Базовый констуктор всегда вызываем самым первым и прописываем в нём имя таблицы

            $this->NInit('code',      NT_CONST, 'Артикул');
            $this->NInit('name',      NT_STR,   'Название');
            $this->NInit('price_rub', NT_FLOAT, 'Цена рублей');
            $this->NInit('photo',     NT_IMG,   'Фотография');
            $this->NInit('text',      NT_TEXT,  'Описание');

            $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(); // Записать изменения
    

Когда некоторое количество объектов окажется записано в базу, вам понадобится поиск/извлечение элементов. Он реализован через метод Find(). Получить все существующие объекты:

       
        $model = new ItemNodeModel();
        $total = 0;
        $ids = $model->Find($total); // Здесь получаем id всех объектов.
    

Более сложный случай:


        $filter = array();
        $filter["price_rub"] = 1999.95; // Будем искать все объекты у цена равна 1999.95
                   
        $from  = 0;   // Начиная с какого элемента
        $count = 100; // Сколько элементов извлечь
       
        $model = new ItemNodeModel();
        $total = 0; // Сюда будет записано общее количество подходящих элементов
        $ids = $model->Find($total, $filter, $from, $count, "price_rub"); // Здесь получаем id найденых элементов. Полученные элементы будут упорядочены по полю price_rub.
    

Теперь имя id элементов можно создать и сами объекты:


        $all = array();
        foreach($ids as $id)
        {
            $all[] = ItemNodeModel($id);
        }
    

С найдеными элементами можно выполнять любые операции, в том числе вывести в шаблоне:

        
    <?php foreach ($all as $a):?>
  • <?= "ID объекта: {$a->_id}, Артикул: {$a->code}, Цена: {$a->price_rub}"?>
  • <?php endforeach?>

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

Несколько значений у одного поля

У любого поля, используя индекс, можно задать (и прочитать) более одного значения:


        $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?>

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

        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); // Число покупателей может быть до трёх
            }
        };
    

Но на практике эта величина влияет только на компонент админка для модели атрибутов. При записи/чтении данных это ограничение не работает.
Когда количество задано - в административном разделе можно добавлять дополнительные значения для поля, но не больше чем заданное число.
Не ставьте это число слишком большим - в силу технических причин это может привести к замедлению работы административного раздела во время редактирования объекта.

Типы полей объекта

Как вы заметили инилицализация полей объекта осуществляется через метод NInit. Он принимает 3 параметра:

В компоненте существуют следующие типы:

Тип перечисление 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->Render("sex"); // Выведет 'Женский'
    

Тип 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_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 с обрезкой картинки до точного разрешения
    

Подробнее о параметрах 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); // Выведет ссылку на файл
    

Тип NT_BOOL

Пример использвания на сайте:

        $item = new ItemNodeModel();

        $item->is_show = true;
        echo $item->is_show;  // Вернет 1
        echo $user->Render("is_show"); // Выведет 'Да'

        $item->is_show = false;
        echo $item->is_show;  // Вернет 0
        echo $item->Render("is_show"); // Выведет 'Нет'
    

Тип NT_UDATE

Пример использвания на сайте:

        $item = new ItemNodeModel();

        $item->date = 1403896653;
        echo $item->date;  // Выведет 1403896653
        echo $item->Render("date"); // Выведет '27.06.2014'
    

Остальные типы

Остальные типы достаточно просты и не требуют подробного пояснения. Присвоение значений происходит так же. Полученное значение всегда аналогично присвоенному, даже при использовании метода Render().
Заметим лишь, что при работе с языко-зависимыми типами (NT_STR, NT_TEXT), важно то, какой язык задаётся в конструкторе класса (последний параметр, по умолчанию равен LANG).
При необходимости язык можно менять на ходу:

        $o = new PainterNodeModel();

        $o->SetCurLang('en');
        $o->name = $nameEn;

        $o->SetCurLang('de');
        $o->name = $nameDe;

        $o->Flush();
    

Возможности поиска элементов

Метод Find() предоставляет широкие возможности по поиску нужных объектов в базе. Метод имеет следующие аргументы:

Аргумент $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"
    

Ключ может указывать на место поля в иерархии полей класса. Например, для класса:

        class ItemNodeModel extends AttrComblexModel
        {
            public function __construct($attr_id = NULL, $onlyShow = false, $lang = LANG)
            {
                parent::__construct($attr_id, $onlyShow, $lang); // Базовый констуктор всегда вызываем самым первым
       
                $this->InitAttr('code',    'AConst', 'Артикул');
                $this->InitAttr("preview", 'AMImg',  'Превью изображение'); // У AMImg есть поля file и name
            }
        }
    

Поиск всех объектов, у которых есть превью изображения c расширением "png" будет выглядеть следующим образом:


        $filter = array();
        $filter["preview.file"] = array('like' => ".png"); // У поля preview подполе file должна быть подстрока содержащая ".png"
        $total = 0;
        $ids = $model->Find($total, $filter);
    

ВАЖНО!!! Помните что ключ дожен быть реальным полем и что виртуальные атрибуты не учитываются.

Аргумент $from - начиная с какого элемента вернуть список

Аргумент $count - сколько элементов должно быть в списке

Аргумент $orderBy - название поля, по которому нужно упорядочить элементы. Примеры принимаемых значений:

Если сортировка идёт по полям с типами STR или TEXT, то сортируем по текущему языку объекта.

Аргумент $lang - для строковых параметров позволяет задать язык поиска. По умолчанию равен текущему языку.