Solution Technologies
Управление сайтом без ограничений

Руководство разработчика XS2

Скачать pdf-версию.

Часть 2. Решения на платформе XS2

Глава 11. Триггеры.

Триггером называется метод XS2, выполняющийся на узле при его вставка, редактировании, удалении, копировании или перемещении. Многим хорошо знакомы триггеры, использующиеся в базах данных при вставке, изменении или удалении записи. Аналогия с триггерами в БД в данном случае очевидна и полезна. Практически ни один из серьезных веб-проектов при реализации его на платформе XS2 не обходится без триггеров.

Для демонстрации мы выбрали одну из самых распространенных задач, решаемых с помощью триггеров, - генерацию превью изображений. Представим, что на нашем сайте есть тип узла "photo", с помощью которого в системе представлены фотографии, загружаемые пользователем в свою личную фотогалерею. Среди прочих полей у данного типа имеются два поля типа "image": "Thumbnail" и "FullImage". При добавлении нового узла мы будем сохранять файл фотографии, посланный пользователем, в поле "FullImage", а в поле "Thumbnail" помещать сгенерированное налету превью размером 150 пикселей по большей стороне.

Для того, чтобы создать триггер, необходимо добавить новый метод на произвольном типе узлов. В форме добавления/изменения метода необходимо галочками указать, при каком из пяти действий будет срабатывать триггер. В версии 2 платформы XS2 существует также возможность указать, будет ли срабатывать триггер до или после выполнения действия. В версии 1 платформы XS2 триггер всегда вызывается после действия, и это накладывает определенные ограничения на его функциональность. Во версии 2 разработчик имеет возможность назначить один и тот же метод триггером для нескольких типов. В версии 1 для повторного использования одного метода в разных триггерах можно воспользоваться функцией xs2Fetch.

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

  1. Он вызывается автоматически во время определенных действий, производимых с узлом
  2. Он может быть прикреплен только к типу узла
  3. В нем доступна переменная $THE['NODE'], содержащая текущий узел, с которым производится действие, и некоторые дополнительные поля

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

Итак, добавим триггер с произвольным именем и прикрепим его к нашему типу "photo". Галочками отметим, что он должен выполнятся при вставке нового узла (действие "UPDATE"). Далее напишем в нем следующий код:

Листинг 23
1.  <?
2.    if($_THE['NODE']['FullImage'] && ¶
                 !$_THE['NODE']['Thumbnail']){
3.      $_THE['NODE']['Thumbnail'] = xs2u_imgResize(array(
4.                    'image'=> $_THE['NODE']['FullImage'],
5.                    'size'=>150,
6.                    'dimension'=>1));
7.    }
8.  ?>

В листинге используется функция xs2u_imgResize из XS2 API, которая создает превью на основе полной версии. Полное описание этой функции можно посмотреть в справочнике XS2 API.

Рассмотрим дополнительные поля, доступные в $_THE['NODE']:
В первой версии XS2 в триггере через переменную $_THE['NODE'] доступны следующие дополнительные поля, помимо полей узла:

  • $_THE['NODE']['EVENT'] - название текущего действия, производимого над узлом. Это поле может принимать следующие строковые значения: "insert" (вставка узла), "update" (редактирование узла), "delete" (удаление узла), "copy" (копирование узла), "move" (перемещение узла в другого родителя)
  • $_THE['NODE']['PARAMS']['NodId'] - это поле доступно только при действии "copy" и содержит идентификатор исходного узла, копией которого является текущий. Обратите внимание, что поле $_THE['NODE']['NodId'] при этом содержит идентификатор только что вставленной копии.
  • $_THE['NODE']['PARAMS']['ParId'] - это поле доступно только при действии "move" и содержит идентификатор узла, который был родительским для текущего узла до его перемещения. Обратите внимание, что поле $_THE['NODE']['ParId'] содержит идентификатор родителя, в который только что переместился текущий узел.

Во второй версии XS2 доступны следующие дополнительные поля:

  • $_THE['NODE']['EVENT'] - название текущего действия, производимого над узлом. Это поле может принимать следующие строковые значения: "insert" (вставка узла), "update" (редактирование узла), "delete" (удаление узла), "copy" (копирование узла), "move" (перемещение узла в другого родителя)
  • $_THE['NODE']['POINT'] - точка (момент) выполнения триггера. Данное поле принимает следующие строковые значение: "before" (до действия), "after" (после действия). С помощью этого поля можно узнать, выполняется ли текущий триггер до или после действия.
  • $_THE['NODE']['OLD'] - содержит образ текущего узла, каким он был до начала текущего действия. Таким образом, данное поле содержит в себе в том числе и ту информацию, которая в первой версии передавалась через поля $_THE['NODE']['PARAMS']['NodId'] и $_THE['NODE']['PARAMS']['ParId']. Эти поля сохранены во второй версии платформы для обратной совместимости, однако их использование нежелательно.

Во второй версии XS2 разработчик может определять точку вызова триггера: до или после действия. Важно помнить, что в триггерах, выполняющихся до действия (претриггер), все изменения, внесенные в $_THE['NODE'], не сохранятся. Таким образом изменения полей узла возможно только в триггере, выполняющемся после действия (посттриггер). Претриггер используется чаще всего для проверки данных, предназначенных для сохранения в узле. Поскольку действие еще не произошло, в претриггере можно отменить его, выбросив исключение XS2_Exception() с произвольным текстом ошибки. Приведем пример отмены вставки узла. Следующий листинг содержит код претриггера, выполняющегося до вставки нового узла типа "Пользователь". Он проверяет, есть ли в системе пользователь с таким же электронным адресом и, если есть, отменяет вставку узла.

Листинг 24
1.  <?
2.  $email = $_THE['NODE']['email'];
3.  $users = xs2GetNodes(array(
4.          'mod'=>'users',
5.          'NType'=>'user',
6.          'condition'=>"email='".$email."'"));
7.  if(count($users)>0){
8.    XS2_Exception("Пользователь с электронным адресом ¶
                 ".$_THE['NODE']['email']." уже ¶
                 зарегистрирован в системе.");
9.  }
10. ?>

Одна из потенциальных опасностей использования триггеров - возникновение цикличных вызовов. Рассмотрим следующий листинг. Это триггер, который выполняется при создании нового узла типа "category":

Листинг 25
1.  <?
2.  xs2InsertNode(array(
3.    'mod'=>'catalog',
4.    'NType'=>'category',
5.    'NFlag'=>1,
6.    'NName'=>'Подкатегория'
7.  ));
8.  ?>

Суть этого триггера заключается в том, что он создает дополнительную подкатегорию при создании категории. Казалось бы, ничего страшного в коде нет, но давайте представим, что произойдет при добавлении новой категории. Сработает наш триггер и добавится новая подкатегория, однако при ее создании снова сработает триггер и создастся подкатегория подкатегории. Создание подподкатегории снова приведет к вызову триггера и т.д. Возникнет бесконечный цикл или клинч (deadlock), весьма опасный для функционирования системы. Для того, чтобы этого не происходило, в XS2 запрещен вызов триггеров внутри других триггеров. То есть код, приведенный в Листинге 25, в XS2 сработает как ожидается, - будет создана всего одна подкатегория.

Иногда защита от цикличного вызова триггеров в XS2 становится причиной замешательства разработчиков. Забыв о ней, они рассчитывают на цикличность выполнения триггеров, самостоятельно контролируя клинч. В результате они тратят большое количество времени на поиск причины, по которой триггер не срабатывает внутри другого триггера. Причина же - встроенный контроль клинча XS2. Как же тогда добиться эффекта цикличности, когда это, действительно, необходимо? В большинстве случаев цикличный вызов многих триггеров можно заменить простым перебором необходимых узлов. Приведем пример. У нас есть большой каталог товаров. При добавлении новой категории небходимо увеличить значение поля "ChildrenCounter" на единицу у всех ее категорий-предков. Если бы в XS2 не было защиты от клинча, то код триггера, срабатывающего при добавлении и - что очень важно - изменении категории, выглядел бы так:

Листинг 26
1.  <?
2.  $parent = xs2GetParent(array('mod'=>'catalog', ¶
                 'NodId'=>$_THE['NODE']['NodId']));
3.  $parent['ChildrenCounter']++;
4.  xs2UpdateNode($parent);
5.  ?>

Однако, XS2 заблокирует вызов триггера внутри другого триггера, и такой код приведет к тому, что счетчик увеличится только у непосредственного родителя текущей категории. Решить поставленную задачи можно следующим образом:

Листинг 27
1.  <?
2.  $ancestors = xs2GetPath(array('mod'=>'catalog', ¶
                 'NodId'=>$_THE['NODE']['NodId']));
3.  foreach ($ancestors as $ancestor){
4.    if($ancestor['NodId']!=$_THE['NODE']['NodId']){
5.      $ancestor['ChildrenCounter']++;
6.      xs2UpdateNode($ancestor);
7.    }
8.  }
9.  ?>

Мы просто вернули всех предков текущего узла с помощью функции XS2 API xs2GetPath и пересчитали счетчик для каждого из них.

Мы рассмотрели триггеры - один из мощнейших инструментов добавления собственной функциональности в XS2. Однако большие возможности триггеров влекут за собой и большую ответственность. Разрабатывая код триггеров, необходимо помнить о том, что он, скорее всего, будет использоваться достаточно часто, т.е. для каждого узла данного типа, поэтому он должен быть предельно оптимален.

Введение

Основные характеристики системы XS2
Требование к хостинг-площадке, на которой устанавливается XS2.
Инструкции по установке XS2 на хостинг-площадке.

Часть 1. XS2 шаг за шагом

Модель данных в XS2: модуль, тип, узел, метод.
Как организовать простейшую структуру данных в XS2 и вывести узлы на главной странице сайта.
Множественный выборки узлов и их вывод на странице в цикле.
Создание второстепенной страницы и организация ссылок на нее.
Структура метода в XS2. Что такое процессор и как он взаимодействует в шаблоном внутри одного метода.
Дерево как универсальный способ организации узлов в XS2. Как настраивать вложенность узлов друг в друга.
Ссылки между узлами как основное средство построения альтернативных структур узлов.
Использование функция XS2 API для выполнения редакторских операций в коде методов.
Функция xs2Fetch и ее возможности при организации повторного использования методов.
Подробнее о взаимодействии процессора и шаблона в одном методе: передача данных между процессором и шаблоном.

Часть 2. Решения на платформе XS2

О чем пойдет речь во второй части, для кого она предназначена.
Как производить дополнительные манипуляции с узломпри его вставка, редактировании, удалении, копировании или перемещении.
Постраничный вывод как способ показа больших массивов данных в одном шаблоне.
Как с помощью XS2 быстро сделать сайт с поддержкой множества языковых версий.
Быстрое развертывание удобных пользовательских интерфейсов с технологией AJAX на платформе XS2.
Подробнее о том, как организовать сложные структуры данных в XS2 и производить в них поиск.
В следующих главах, которые будут публиковаться здесь по мере их написания, мы планируем рассказать о:
Copyright ©1998-2008 Солютекс. Все права защищены.
Этот сайт сделан в designLab | Работает на технологии XS2
Условия использования | О защите конфиденциальности