Перейти к содержимому


Фотография
* * * * * 1 Голосов

Краткий курс по API компонента Zoo (JBZoo)

документация api zoo jbzoo helper event модель фреймворк

Сообщений в теме: 9

#1 SmetDenis

SmetDenis

Отправлено 06 September 2013 - 18:19

*
Популярное сообщение!

Я собирался написать эту статью очень-очень давно.
Но в силу различных причин и обстоятельств она откладывалась в долгий ящик.

Предупрежу, что если вы не знаете как пользоваться PHP, то статья явно не для вас, дальше можно не читать.
Остальным - welcome!

Большим плюсом будет если вы понимаете ООП, чем модель отличается от вида и контроллера, ну и респект и уважуха если способны отличить Абстрактную Фабрику от Моста и Пула Одиночек (в реалиях Joomla это я лукавлю конечно, но все же все тоже встречается в глубинах движка… =) ).

Внутреннее API у Zoo и JBZoo (далее просто Фреймворк) довольно богатое, за раз обо всем не рассказать. Как говориться "Нельзя перепрыгнуть пропасть в два прыжка. Поэтому мы будем двигаться через неё маленькими шажочками!"

Статья предполагает пройтись "галопом по Европам" среди основных фич Фреймворка, но 90% материала все же останется в качестве домашнего задания.

И так-с, начнем c малого...

 

Что такое хелперы?

В Zoo (да и вообще в программировании) есть такое понятие, как хелперы (helpers).
Это разнообразные полезные функции, которые сгруппированы вместе по смыслу и образуют небольшие классы. Не стоит путать с библиотеками, под ними обычно подразумевают что-то стороннее и не так сильно интегрированное с системой в целом.

Назначение хелперов самое разнообразное, от глобальных системных (например, для контролирования путей в системе) до периферийных (для работы с аватарами в комментариях).

Важно понимать смысловую разницу между обычным классом и хелпером.
Хелпер имеет ленивую инициализацию и создается автоматически. Это значит что до тех пор пока вы первый раз не обратитесь к хелперу файл не будет подключен и экземпляр не будет создан. Создается обычно только один экземпляр хелпера. По сути, это паттерн ООП "пул одиночек".
Так же еще одна важна особенность, хелпер не должен хранить состояние. Его всегда нужно рассматривать как набор инструментов, а не определённую сущность сайта (для сущностей есть модели). Иначе вам нужно пересмотреть логику вашего расширения.

 
Где лежат хелперы?
Посмотреть их воочию можно по следующим путям. Различные папки нужны в основном только для группировки хелперов. Итак

Системные - /administrator/components/com_zoo/framework/helpers/
Основные от Zoo - /administrator/components/com_zoo/helpers/
Хелперы JBZoo (все имеют префикс "jb") - /media/zoo/applications/jbuniversal/framework/helpers/
Перезагруженные стандартные - /media/zoo/applications/jbuniversal/framework/helpers-std/

 
Как работать с хелперами?
Для работы с хелперами в Zoo предусмотрен класс App. Это своеобразная точка доступа к различному функционалу Фреймворка. Получить доступ к нему можно двумя способами.
 
// В любом месте кода (даже Joomla), аргумент всегда один и тот же и обозначает глобальный наймспейс в Zoo.
$app = App::getInstance('zoo');

// код исполняется в контексте любой сущности фреймворка
$app = $this->app;
Важное замечание, строки выше НЕ копирую объект, а получают ссылку на него. Т.е если вы его измените его в одной части программы, то это коснется и других частей.

Поле вызова одиночки App получаем класс хелпера по имени файла и вызываем его метод.
Например так
/*
   Из /administrator/components/com_zoo/helpers/route.php будет вызван метод item
   Так можно получить ссылку на материал имея на руках нужный экземпляр
*/
$itemUrl = $this->app->route->item($item);
echo '<a href='.$itemUrl.'>Ссылка на страницу</a>';
Полезные примеры с хелперами

Получить объект материала или категории из базы данных по его ID
$item = $this->app->table->item->get(42);
$category = $this->app->table->category->get(42);
Масштабирование произвольной картинки с кэшированием
// встроенная функция для ресайза картинки (только полные (абсолютные) пути)
$thumbFullpath = $this->app->zoo->resizeImage($OriginalFullpath, $width, $height);

// обертка от JBZoo, которая умеет работать с относительными путями, но вернет объект
$thumbInfo = $this->app->jbimage->resize($OriginalPath, $width, $height);

// результат работы
$thumbInfo->orig // полный путь до оригинального файла
$thumbInfo->origUrl // полный путь до оригинального файла для сайта (ссылка)
$thumbInfo->path // полный путь до миниатюры
$thumbInfo->rel // относительный путь до миниатюры
$thumbInfo->url // полный путь до миниатюры для сайта (ссылка)
$thumbInfo->height; $thumbInfo->width // высота и ширина миниатюры
 
Работа с путями
В нашем фреймворке есть такое понятие как виртуальные пути файловой системы.
Суть их работы в следующем, одному и тому же ключу можно указать сразу несколько различных реальных путей. После этого можно обратиться по этому ключу и хелпер автоматически пройдет по всем реальным путям и найдет файл. Штука очень удобная, но сходу не понятна. Попробую объяснить на примере.

Допустим мы хотим узнать полный путь до хелпера для работы с корзиной jbcart
Пишем следующий код
 
 // двоеточие разделяет ключ (имя вирт пути) и относительный путь до файла
$helperPath = $this->app->path->path('helper:jbcart.php');
dump($helperPath); // полный путь до файла jbcart.php
Собственной, что произошло?
Система автоматически регистрирует несколько реальных путей для ключа - "helper"
irr_200x0.png
PathHelper будет искать файл "jbcart.php" в этих путях в обратном порядке.
Когда найдет, вернет полный путь до этого файла.

Таким образом можно решать разные задачи
сокращать длинные пути
проверять существование файлов
перегружать существующие пути и подменять некоторые системные файлы с ленивой инициализацией

Список всей существующих имен для хелпера
g5t_200x0.png

 
Работа со статикой
Хелпер JBAssets нужен для быстрого подключения различной статики, скриптов и их зависимостей.
Например при активации fancbox, jQuery подключится автоматически.



// включение различных библиотек
$this->app->jbassets->jQuery();
$this->app->jbassets->jQueryUI();
$this->app->jbassets->fancybox();
$this->app->jbassets->tablesorter();
$this->app->jbassets->chosen();
$this->app->jbassets->datepicker();
// итд, полный список методов внутри хелпера

// добавить глобальную переменную из PHP в документ HTML
$this->app->jbassets->addVar($varName, $value);

// подключить файл статики, используется виртуальный путь (из хелпера path)
$this->app->jbassets->js('<путь до файла JS>');
$this->app->jbassets->js('<путь до файла CSS>');
 
Хелперы для создания SEF-ссылок
И сразу примеры
 
// как по ID материала получить ссылку на материал
$item = $this->app->table->item->get(42);
$url = $this->app->route->item($item);

// как по ID категории получить ссылку
$category = $this->app->table->category->get(42);
$url = $this->app->route->category($category);

// главная страница каталога
$url = $this->app->route->frontpage($application_id);
Особые страницы из JBZoo, например на страницу с корзиной
$basketUrl = $this->app->jbrouter->basket($menuItemid, $appId);
 
Работа с системным кешем
Кеш на основе Zoo
 
// создаем объект кэширования и передаем путь хранения
$cache = $this->app->cache->create($this->app->path->path('cache:') . '/file_name', true, 86400);

// проверяем права (не обязательно)
if (!$cache->check()) {
    $this->app->system->application->enqueueMessage('Cache not writable!', 'notice');
}

// проверяем актуальность кеша
$cacheСheck = ($cache) ? $cache->check() : false;
if ($cacheСheck) {
    $data = $cache->get('Ключ переменной');
}

if (empty($data)) {
    // сложные вычисления
    $data = 2 + 3;
    
    // сохраняем в кеш
    $cache->set('Ключ переменной', $data);
    $cache->save();
}
или на основе JBZoo (по сути упрощенная обертка вокруг кеша Zoo)
$key = 'Переменная, которая отвечает за уникальность значения кеша';  // ключ кеша
$group  = 'папка/группы/кеша';

// проверяем актуальность
if (!($result = $this->app->jbcache->get($key, $group))) {

    // сложные вычисления
    $result = 2 + 2 * 2;

    // сохраняем результат
    $this->app->jbcache->set($key, $result, $group);
}
 
Работа с данными из REQUEST
$request = $this->app->jbrequest;

// получить данные из внешней переменной (очищена от HTML и пробелов по краям)ж
// второй аргумент освобождает от лишних проверок в коде
$var = $request->get('varName', 'defaultData');

// узнать, текущий запрос сделан AJAX'ом или он обычный
if ($request->isAjax()) { … }

// Это POST запрос?
if ($request->isPost()) { … }

// Переменная option ровна com_zoo? (избавляет от простейших условий в коде)
if ($request->is('option', 'com_zoo')) { … }
Хелперов внутри фреймворка десятки. Если рассказать о каждом, то получится небольшая книжка =)
Поэтому оставлю это на самостоятельное изучение и перейду к моделям.
  • 5
JBZoo v4.0 и новый чудный мир Open Source GPL
Отключайте проверку лицензий как можно скорее!



— Есть два типа людей: Кто еще не делает бекапы и кто уже делает бекапы.


#2 SmetDenis

SmetDenis

Отправлено 13 October 2013 - 00:44

Модели фреймворка
Модели - это набор классов для работы с данными (хранением, обработкой итд). Дополнительно каждый класс обычно представляет конкретную сущность системы (материал, категория, тип итд).
В данном случае мы будем говорить только о тех, которые работают с базой данных компонента.

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

Рекомендуется использовать для работы с базой данных именно эти сущности. Тогда это не разрушит целостности данных в системе и будут учитываться различные события. Говорю это для любителей работать с базой данных чистыми SQL-запросами.

 
Выборки из базы данных
Стандартные вещи с базой данных делаются с помощью хелпера table
Частично об этом я говорил выше, теперь посмотрим более сложные примеры.




// возьмем ссылку, чтобы наш код был короче
$itemModel = $this->app->table->item;
$categoryModel = $this->app->table->category;

// материалы определенной категории
$items = $itemModel->getByCategory($appId, $catId);

// выбрать материалы по автору
$items = $itemModel->getByUser($appId, $userId);

// простая произвольная выборка
// $ids = произвольный набор ID материалов
$conditions = array(
     'id IN (' . implode(',', $ids) . ')'
);

$items = $itemModel->all(compact('conditions')); // привычный для Zoo способ записи
$items = $itemModel->all(array('conditions' => $conditions)); // более понятная запись

// выберем первый материал по алфавиту из каталога id=10
$categories = $categoryModel->all(array(
    'conditions' => array(
        'application_id=?', 10
    ),
    'order' => 'name',
    'limit' => 1,
));

Стандартный конструктор для выборок в Zoo далеко не самый гибкий. Поэтому для мы с помощью JBZoo добавили свои модели. Все они находятся тут /media/zoo/applications/jbuniversal/framework/models/

Рассмотрим пару примеров

$options = array(
    'category_nested' => true, // выбирать из вложенный категорий
    'limit' => array(2, 10), // offset и limit
    'user' => true,  // учитывать ID автора (текущий пользователь)
    'published' => 1, // учитывать состояние публикации
    'order' => 'alpha' // сортировка
);
// $appId, $catId, $typeId - могут быть конкретным значением или массивом
$items = JBModelItem::model()->getList($appId, $catId, $typeId, $options);

// произвольная фильтрация на основе индекса JBZoo
// массив $elements полностью совпадает со структурой запроса из фильтра.
$elements = array(
    '<ELEMENT_ID>' => 'Значение',
);
$items = JBModelFilter::model()->search($elements);

 
Правильное удаление из базы
Материал имеет очень много зависимостей в базе (комментарии, индекс, категории, каталоги, теги и прочее прочее). Эти связи распространяются почти на все таблицы Zoo.

Чтобы правильно удалить материал (либо любую другую сущность) нужно вызывать только методы Zoo.
Иначе у вас в базу будет хранится разнообразный хлам и как следствие - скрытые ошибки базы данных и лишний объем.

Для этих целей у каждого класса таблицы предусмотрен метод delete()

$tableItem = $this->app->table->item;
$item = $tableItem->get(42);
$tableItem->delete($item);

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


Сообщение отредактировал SmetDenis: 19 May 2016 - 15:14
Опечатка.

  • 3
JBZoo v4.0 и новый чудный мир Open Source GPL
Отключайте проверку лицензий как можно скорее!



— Есть два типа людей: Кто еще не делает бекапы и кто уже делает бекапы.


#3 SmetDenis

SmetDenis

Отправлено 13 October 2013 - 00:50

*
Популярное сообщение!

Системные события фреймворка

Практически в любом фреймворке есть такое понятие как системные событие (event'ы, триггеры, хуки, ловушки). В Joomla они оформлены как плагины, а в классической литературе это добро обычно называют Observer или слушатель.

Смысл в следующем. В разных местах кода расставлены вызовы событий, например после удаления любого материала будет вызвано событие "item:deleted". Все функции, которые подключены к этому событию будут выполнены в порядке подключения. Таким образом будут удалены зависимости от материала, такие как картинки или записи в базе данных.

 
Как это работает?
В рамках Zoo все выполнено довольно в упрощенном варианте (собственно как и во всей Joomla) и собрано все в единственном хелпере event.

Создаем и подключаем класс события. Далее

$event         = $this->app->event;
$dispatcher = $event->dispatcher;

// производим регистрацию своего класса
$event->register('MyEventClass');

// подключаемся к событию
$dispatcher->connect('myevent:function', array('MyEventClass', 'handler'));
Если вы решили сделать собственное событие, то все тоже самое, только в нужный момент не забывайте вызывать нотификацию диспетчера
$dispatcher->notify($event->create($var, 'myevent:function', array( /* ассоц массив с доп параметрами */))); // $var - основной объект события
 
Пример простейшего класса для обработки событий
// Класс нужно подключить вручную
class MyEventClass
{

    /**
     * Simple event handler
     * @param AppEvent $event
     */
    public static function handler($event)
    {
          $app = App::getInstance('zoo'); // не забываем, что это статический метод, где нет $this
          $subject = $event->getSubject(); // получаем объект события
          $params = $event->getParameters(); // получаем доп параметры
         
    }

}
 
Существующие события системы
JBZoo регистрирует классы "пустышки" для удобства расширения функционала
Их можно найти тут - /media/zoo/applications/jbuniversal/framework/events/jbevent.*.php
Заодно так можно узнать как какие события есть в системе.

 
Важно понимать что
  • Обработчики событий - это только статические классы
  • Все объекты передаются по ссылке, т.е их изменения коснутся остального кода.
  • Не важно что вы вернете из события - это нигде не используется.
  • Цепочку действий в событие не прервать
  • Можно легко уйти в цикл, например в init вызывать создание аналогичного экземпляра.

  • 5
JBZoo v4.0 и новый чудный мир Open Source GPL
Отключайте проверку лицензий как можно скорее!



— Есть два типа людей: Кто еще не делает бекапы и кто уже делает бекапы.


#4 SmetDenis

SmetDenis

Отправлено 07 February 2014 - 12:01

*
Популярное сообщение!

Как получить доступ к данным произвольного поля материала ?

например, вне шаблона в произвольном месте

<?php
// если у вас нет материала (переменной $item), но вы знаете его ID
 
$app = App::getInstance('zoo');
$item = $app->table->item->get('42'); // берем материал id=42
 
$element = $item->getElement('8238cb42-b699-4760-9503-6a90fb19d45e'); // element id получаем так 
$data = (array)$element->data(); // получаем данные
print_r($data); // смотрим что там хранится
 
// наиболее частые примеры хранения данные в элементе
echo $data['value'];
echo $data[0]['value'];

Как изменить одно поле программно?

// в продолжение предыдущего примера
 
$element->bindData($data); // сохраняем данные обратно в элемент
$app->table->item->save($item); // сохраняем изменения в базу, переиндексация материала произойдет автоматически

Как узнать Element ID поля (элемента) ?

В настройках элемент можно найти вот такую запись

6z6_200x0.png


  • 5
JBZoo v4.0 и новый чудный мир Open Source GPL
Отключайте проверку лицензий как можно скорее!



— Есть два типа людей: Кто еще не делает бекапы и кто уже делает бекапы.


#5 SmetDenis

SmetDenis

Отправлено 05 March 2014 - 22:14

*
Популярное сообщение!

Как программно создать новый материал?

Пример создания нового материала через PHP
// пустышка
$item                   = $this->app->object->create('Item');

// наполняем стандартные поля
$item->application_id   = 10;
$item->name             = 'Item name';
$item->alias            = 'item-alias';
$item->type             = 'type-alias';
$item->publish_up       = $this->app->date->create()->toSQL();
$item->publish_down     = $this->app->database->getNullDate();
$item->created          = $this->app->date->create()->toSQL();
$item->created_by       = JFactory::getUser()->get('id');
$item->created_by_alias = '';
$item->state            = 1;
$item->searchable       = 1;

// наполняем пользовательские поля
$item->getElement('<element_id>')->bindData(array(
  'value' => '123' // данные в формате элемента, можно посмотреть PMA
));

// сохраняем
$this->app->table->item->save($item);

  • 5
JBZoo v4.0 и новый чудный мир Open Source GPL
Отключайте проверку лицензий как можно скорее!



— Есть два типа людей: Кто еще не делает бекапы и кто уже делает бекапы.


#6 CB9TOIIIA

CB9TOIIIA

Отправлено 16 November 2016 - 08:02

Дополнение:
 
По ID материала получить ссылку на материал


$this->app->jbrouter->externalItem($item)

Получить артикул (SKU) или цену - без использования GetList()


$CB_price = 'ELEMENT_ID';
$CB_SKU = $this->_item->getElement($CB_price)->data()->variations;
$CB_SKU_App = $this->app->data->create($CB_SKU);

$CB_SKU = $CB_SKU_App->find('0._sku.value');
$Value_Price = $CB_SKU_App->find('0._value.value');

$CB_SKU = trim(strip_tags($CB_SKU));
$Value_Price = trim(strip_tags($Value_Price));

Получить по API главную категорию.

$primary_category_id = $this->_item->getPrimarycategory()->id;

  • 4

#7 CB9TOIIIA

CB9TOIIIA

Отправлено 11 April 2017 - 07:40

Отладка в JBZoo

 

Включить режим отладки на сайте и плагин отладки. Чтобы низу появился профайлер.
После этого найдите файл:

media\zoo\applications\jbuniversal\framework\helpers\jbdebug.php

cjdj_200x0.png

Внизу должно появится много отладочной информации. 


  • 1

#8 mmth

mmth

Отправлено 20 June 2018 - 07:54

Получение родительской категории по id категории

$parent_category_id = $zoo->table->category->get($cid)->parent;
//$zoo экземпляр приложения, $cid идентификатор текущей категории, если категории нет вернет 0

Сообщение отредактировал mmth: 20 June 2018 - 07:55

  • 2

#9 CB9TOIIIA

CB9TOIIIA

Отправлено 01 December 2018 - 00:42

Когда требуется получить кол-во $item в категории:

count($itemModel->getByCategory($appId, $cat))					

Но по скорости и гайдам - лучше использовать:

JBModelItem::model()->getTotal($appId, $typeId, array($cat))

  • 2

#10 CB9TOIIIA

CB9TOIIIA

Отправлено 09 May 2019 - 17:14

JBCart::val($order->getTotalSumRU()->val('RUB'), 'RUB');

Конвертация в рубли из BY например)


  • 1





Темы с аналогичным тегами документация, api, zoo, jbzoo, helper, event, модель, фреймворк

Click to return to top of page in style!