Про процессоры у нас написано много, но вот этот комментарий натолкнул меня на мысль, что некоторые вообще не представляют себе что такое MODX-процессоры и с чем их едят. Учитывая то, что практически все наши разработки (в том числе и сборка ShopModxBox) основываются на работе процессоров, я решил написать эту статью, в которой постараюсь максимально подробно раскрыть тему процессоров. Если вы не понимаете процессоров, у вас никогда не получится нормально тюнинговать сборку ShopModxBox под себя, так что советую максимально четко изучить данный материал (для этого будет приведено множество примеров). Обязательно попробуйте выполнить представленные примеры самостоятельно и понять как они работают. Освоите — многое для вас станет понятней и проще. Сразу скажу, что понимание принципов ООП сильно поможет вам в освоении этого материала, так что если у кого пока нет знаний в php-ООП, советую к изучению вот эту страничку. Для начала выполним простейший скрипт (здесь и далее скрипты выполнять будем в компоненте Console в админке ShopModxBox (чтобы точно все примеры работали)). В ответ мы получим примерно такой ответ: Наблюдательные читатели могли заметить схожесть параметров в примере с вызовом процессора в смарти, а именно: и И это не случайно. Фактически, вызывая процессор в Смарти, мы вызываем представленный код, а точнее метод $modx->runProcessor($processor, $params, $options); Так или иначе, и в одном и в другом случае мы получим ответ в одном и том же формате — массиве (на самом деле ответ может быть не только в виде массива, но мы будем рассматривать здесь стандартный ответ). Для начала разберем вызов процессора. $action = 'web/catalog/products/hot/getdata'; $action — это путь до вызываемого процессора. В нашем случае это вот этот процессор. На то, в какой папке будет выполняться поиск процессора, отвечает элемент 'processors_path', который содержит путь до папки процессоров. Здесь для нас важную роль играет параметр $ns = 'modxsite'; Это название пространства имен MODX-а (как правило пространства имен создаются автоматически при установке компонентов, а управление ими доступно в главном меню Настройки — Пространства имен). В нашем случае мы получаем путь до процессоров неймспейса modxsite (в целом, можно использовать синоним Компонент, но всетаки компонент и неймспейс — это не одно и то же). Бывает, что у нас вызываются процессоры и из других компонентов, например здесь вызывается процессор из компонента basket, который, как наверняка многие знают, отвечает за работу корзины. И третий параметр — $params, который содержит передаваемые в процессор параметры. Внутри процессора эти параметры будут доступны через метод $this->getProperty() или в массиве $this->properties. В нашем случае, передав в процессор параметр 'limit' => 6, мы ему «указали», что надо получить максимум 6 записей. А теперь попробуйте в своем Смарти-шаблоне прописать такой код: Теперь на своей странице, где будет вызван этот код, вы получите такой же ответ, как и в админке в консоли. Вот именно с этим ответом чаще всего и приходится работать, в нем содержится информация о полученных данных и их, как правило, мы используем для того же вывода списка товаров. Давайте рассмотрим данные ответа. Сначала стандартные элементы: success => 0 || 1. Флаг успешного или не успешного выполнения процессора. Под не успешным подразумевается случай, когда процессор выполнен, но в нем возникли ошибки (например, мы прописали проверку каких-то обязательных полей, и обрабатывая запрос при отсутствии необходимых данных возвращаем ошибку с сообщением заполнить необходимые поля). message. Сообщение, возвращаемое процессором (чаще всего методами $this->success($message) или $this->failure($message)). Сообщение может отсутствовать. count — количество полученных записей. total — количество всего записей, соответствующих условиям выборки данных. limit — лимит на количество получаемых записей. object — массив данных полученных записей. Это были стандартные поля, которые как правило возвращаются любым MODX-процессором. Параметр page — это уже добавленный нами в процессоры компонента modxsite, чтобы удобней было работать с постраничностью. Общее количество записей (total), количество записей на одну страницу (limit) и номер текущей страницы (page) — это все то, что нам нужно знать для формирования постраничности. К слову, если вызывать шаблон постраничности pagination.tpl, передав в него полученный ответ процессора $result, вам будет сформирован HTML-код постраничности. Пример реализации можно подсмотреть здесь. Так откуда же растут ноги у этих процессоров? На самом деле все MODX-процессоры так или иначе берут начало от главного класса — modProcessor. Все базовые MODX-процессоры прописаны в modprocessor.class.php. Перечислим их: modProcessor. Этот класс является абстрактным (объявлен как abstract class modProcessor), то есть его нельзя вызывать напрямую, можно только расширить его другим классом и вызывать уже тот класс. В нем прописаны все базовые методы. Разберем основные: setProperty($k,$v). Устанавливает свойства процессора (добавляет в массив $this->properties переменные со своими значениями (за один вызов только одно значение)). $this внутри объекта — это магическая переменная, ссылающаяся на сам объект. К примеру, если вы хотите внутри процессора получить его свойства, вы в нем прописываете $properties = $this->getProperties(); getProperty($k,$default = null). Получает свойство процессора. unsetProperty($key). Удаляет свойство процессора. Это имеет смысл когда вы хотите избежать переопределение какого-либо свойства объекта. Дело в том, что все передаваемые в процессор параметры попадает в его свойства $this->properties, и, к примеру, если у вас выполняется обновление объекта в рамках update-процессора modObjectUpdateProcessor (который мы чуть подробней рассмотрим ниже), в свойства полученного объекта передаются полученные свойства процессора. К примеру, если вызовем процессор на обновление документа так: $modx->runProcessor('resource/update', array('id'=> 1, 'pagetitle' => 'new pagetitle'));, то будет получен объект документа с id => 1, и его заголовок (pagetitle) изменен на переданный в процессор параметр 'pagetitle' => 'new pagetitle'. Так вот, если мы в своем процессоре хотим избежать того, что кто-то передаст новый заголовок и и заголовок документа изменится, мы в функции initialize() можем прописать $this->unsetProperty('pagetitle'); Таким образом даже если кто-то и передаст в запрос параметр pagetitle, он будет удален из параметров. setProperties($properties). Устанавливает сразу несколько свойств процессора. getProperties(). Получает все параметры процессора (на самом деле возвращает массив $this->properties). setDefaultProperties(array $properties = array()). Так же как и setProperties($properties), устанавливает параметры процессора, но с той лишь разницей, что он не замещает уже имеющиеся параметры. То есть если, к примеру, при вызове процессора был передан параметр sort, а в процессоре вызывается $this->setDefaultProperties(array('sort' => 'id',)), то переданный параметр sort не будет затерт устанавливаемым дефолтным значением. checkPermissions() Проверяет доступ к вызываемому процессору. К примеру, в нем можно прописать так: Таким образом только если пользователь будет авторизован (объект пользователя $modx->user содержит значение id), а так же родительский процессор вернет успех на проверку checkPermissions(), тогда только будет возвращено true, и значит процессор может выполняться. Иначе будет возвращено Access denied. initialize(). В этом методе как правило прописывается проверка передаваемых данных, установка дефолтовых значений и т.п. В нем позволительно возвращать не только true|false, но и просто текстовое сообщение. Данное сообщение будет так же расцениваться как ошибка и будет содержаться в параметре message описанного выше формата ответа. Рассмотрим довольно типичный код: Здесь мы установили значение по умолчанию «subject» => «Заказ звонка с сайта» (Оно может быть переопределено входящим параметром в вызове процессора или в методе initialize() расширяющего процессора), затем проверили значения name и phone, чтобы были заполнены, и в случае если какой-то из них не заполнен ($this->addFieldError() добавляет ошибку, а $this->hasErrors() проверяет есть ли ошибки в процессоре), вернули ошибку. success($msg = '',$object = null). Возвращает успешный ответ выполнения процессора (в массиве ответа тогда параметр success содержит true). failure($msg = '',$object = null). Возвращает ошибку. Методы success() и failure() как правило возвращаются в методе process(). hasErrors() как и говорилось выше, проверяет есть ли ошибка в процессоре или нет. Здесь есть одно очень важное замечание: как я уже не раз говорил, у процессоров нет собственного объекта обработки ошибок, поэтому, если у вас вазывается сразу несколько процессоров, и в каком-то из них будет ошибка, то все последующие процессоры при проверке $this->hasErrors() будут возвращать ошибку, так как на все один единый объект — $modx->error. Так что желательно после вызова процессора выполнять сброс ошибок $modx->error->reset(). В случае, если процессор вызывается в Smarty-шаблоне, такой сброс ошибок не требуется, так как сброс прописан в самом смарти-плагине (modxSmarty v1.0.0+). addFieldError($key,$message = ''). Добавляет множественное сообщение об ошибке (удобно, к примеру, при проверке нескольких полей формы). getLanguageTopics(). Здесь мы можем указать массив словарей, которые нужно будет MODX-у инициализировать перед выполнением процессора. process(). Этот метод в базовом классе абстрактный, что обязывает в расширяющих процессорах прописать собственный метод process() с пользовательским кодом. Если этот метод не прописать, будет возвращена фатальная ошибка. К примеру, в расширяющем его процессоре modObjectCreateProcessor прописан свой метод process(), так что если ваш процессор расширяет его, то метод process() уже не обязательно прописывать. run(). Это самый главный метод процессора, который задает общую логику работы процессора, выполняя основные его методы в нужном порядке и обрабатывая ошибки. Давайте рассмотрим его код внимательней с комментариями. Этот базовый метод задает стандарт выполнения любого MODX-процессора и его ответа. Метод run() в принципе не принято переопределять (это тот один из немногих случаев, когда я бы методу задал атрибут final). Вот, собственно, отсюда и пляшут все расширяющие процессоры. Давайте продолжим перечислять основные. modObjectProcessor расширяет modProcessor и так же является абстрактным классом, то есть его нельзя вызывать напрямую. Он устанавливаем базовые свойства для нескольких типовых дочерних классов, выполняющими действия с xPDO-объектами: modObjectGetListProcessor. Получает массив xPDO-объектов (к примеру, массив пользователей). modObjectCreateProcessor. Создает xPDO-объект (к примеру, новый документ). modObjectUpdateProcessor. Обновляет xPDO-объект. modObjectDuplicateProcessor. Создает копию xPDO-объекта. modObjectRemoveProcessor. Удаляет xPDO-объект. modObjectSoftRemoveProcessor. Обновляет xPDO-объект, отмечая его как удаленный (устанавливает свойства deleted => 1). modObjectExportProcessor Экспорт xPDO-объекта в XML (для последующего скачивания). modObjectImportProcessor. Импорт xPDO-объекта их XML. Для всех Object-процессоров важен параметр $classKey, который должен содержать название xPDO-класса (в дальнейшем объекта). К примеру, если вы вызываете modObjectCreateProcessor, который должен в итоге создать новый объект пользователя (modUser), то надо в этом параметре прописать значение 'modUser'. Смотрите как это сделано в системном modUserCreateProcessor. К слову, последние два процессора помогли бы вот в этом вопросе, но, к сожалению, в этих процессорах не прописана подгрузка зависимых объектов, что не позволяет выгрузить документ товара вместе со всеми данными товара. На досуге подумаю на счет такого механизма. Советую посмотреть вот этот ресурс: fossies.org/dox/modx-2.3.3-pl/modprocessor_8class_8php.html /assets/images/resized/2015/1563/4e83a5ece6.jpg /assets/images/resized/2015/1563/2e1c3890dc.jpg /assets/images/resized/2015/1563/9b1a6ead88.jpg Вообще, если освоить навигацию по тому ресурсу и хорошенько покопаться, можно много всего найти и узнать. К примеру, можно ощутить, что MODX со своими процессорами — это целая вселенная! /assets/images/resized/2015/1563/513a9fb28b.jpg Здесь видна лишь маленькая часть имеющихся в MODX-е процессоров, которые так или иначе все являются предками главного класса modProcessor. Это процессоры, которые отвечают за создание/редактирование/удаление и т.п. практически всех сущностей в нем (документы, пользователи, контексты, настройки, ТВ-параметры и т.д. и т.п.). Это реально очень мощный механизм, который никак нельзя обходить стороной. Любителям сниппетов я бы сказал так: сам механизм управления сущностями в MODX-е не построен на сниппетах или типа того. Только процессоры. Поэтому если вы хотите разрабатывать действительно мощные веб-проекты, без процессоров вам никак не обойтись (хотя правильней сказать одними сниппетами вам не обойтись). Ну а теперь рассмотрим процессоры из компонента modxsite (напомню, что практически все наши getdata-процессоры в сборке основаны на них). Самый основной из них — modSiteWebGetlistProcessor. Он расширяет родной MODX-овый процессор modObjectGetListProcessor, но несколько переопределяет и дополняет его логику работы. К примеру, в нем предусмотрено кеширование результатов выборки. Если передать в вызов параметр cache => 1, то результаты выборки будут закешированы, и при повторном вызове данные будут браться из кеша, а не опять формировать запрос к базе данных, выполнять его и обрабатывать. Тут оговорюсь, что в формировании ключа кеша учитываются все входящие параметры процессора, так что для запросов, к примеру, с разными параметрами page будут сформированы разные кеш-результаты. В целом этот процессор можно особо не копать (достаточно просто знать что он есть, какие параметры принимает и что возвращает), ибо логика в нем местами запутанная, но если у вас хорошие знания php и вы планируете создавать не один проект на базе ShopModxBox, то тогда поковырять его хорошенько будет очень даже полезно. Второй процессор — modSiteWebGetdataProcessor, расширяющий modSiteWebGetlistProcessor. Этот процессор, в отличие от своего родителя (который сам по себе на самом деле редко используется), оперирует не с xPDO-объектами, а с чистыми данными из таблицы указанного в $classKey классе. Объясню: modSiteWebGetlistProcessor получает коллекцию объектов методом $modx->modx->getCollection(), то есть не просто получает данные из БД, а создает на основе этих данных xPDO-объекты. Это может быть необходимо, чтобы проверить права пользователя на эти объекты средствами MODX-а, которые требуют наличия самого объекта (а не данных его в БД, на основе которых на самом деле на уровне БД нельзя выполнить проверки (во всяком случае родной механизм MODX-а этого не предусматривает)). Но инициализация объектов требует во много раз больше ресурсов, чем просто получить данные этих объектов из БД, поэтому чаще всего мы используем именно getdata-процессор, если нам нужны просто данные записей и мы знаем заранее, что они не требуют специальных проверок на доступ (к примеру, если каталог публичный, без всяких лишних требований к пользователям, то нам просто надо получить данные этих записей и все. Без инициализации объектов все будет выполнено гораздо быстрее). Другое важное отличие getdata-процессора от getlist-процессора — это обработка множественных записей TV-полей (в случае выполнения выборки их в запросе) и набивка этих данных в уникальный элемент документа в общем массиве данных. Просто, насколько наверняка многим известно, данные TV-параметров страниц находятся в отдельной таблице от документов и связь их один-ко-многим, то есть на одну запись документа может содержаться несколько записей TV-полей. Здесь все эти данные TV-полей будут набиты в массивы tvs для каждого документа в отдельности. В скором времени скорее всего этот блок кода перекочует в свой, более узкопрофильный процессор для получения документов — modSiteWebResourcesGetdataProcessor А теперь давайте закрепим наши теоретические знания практическими, выполнив несколько упражнений. 1. Создадим новый документ. Для этого воспользуемся родным MODX-процессором resource/create. Все исполняемые процессоры самого MODX-а находятся в папке MODX_PROCESSORS_PATH Выполним его. Если у вас все хорошо выполнилось, вы получите ответ, содержащий success => 1 и id созданного документ, типа такого: Иначе будет ошибка, например такая: 2. Обновим существующий документ. Для этого нам надо будет вызвать процессор resource/update. Единственно, в случае с обновлением документа не достаточно будет передать только его id-шник и изменяемые поля, так как в процессоре часть кода основывается на передаваемых данных, а не на данных полученного объекта документа, но это не беда. 3. Получим данные товаров в каталоге Для этого вызовем процессор web/catalog/products/getdata самой сборки ShopModxBox. В ответ вы должны получить массив данных товаров (максимум трех по условию запроса), отсортированных по дате создания в обратном порядке. На этом на сегодня все. А в качестве домашнего задания попробуйте проследить всю родословную процессора из последнего примера, и понять как формируется запрос на получение данных и какие таблицы он затрагивает.

Аа, ну если F5 — так это все ОК. 1. Не надо нажимать F5 — это не действие, предусмотренное какой-то ссылкой на странице или типа того. 2. Обработка ошибки здесь — это норма. Попытка очистки несуществующей корзины — это ошибка, и ее надо обрабатывать. Другое дело, что вы видите системное сообщение на странице типа [2015-02-09 23:18:57] (ERROR @ /index.php)..., так это просто плагин Debug работает. Во-первых, он срабатывает только для суперюзеров, простым пользователям не будет выводиться подобное сообщение. Во-вторых, на продакшене этот плагин правильно отключать (он в категории modxSite).

Сборка 2.5, в момент очищения все ок, после f5 эта фигня вылазит, так что можно ничего не делать, позже обновлюсь. Спасибо.

Проблема именно в момент очищения корзины, а после этого при переходе на другую страницу этой ошибки нет? Если ошибка выходит на всех страницах, возможно вы удаляли активные корзины в админке в управлении заказами. Если так, то необходимо сбросить все сессии, так как просто висит в сессии ID несуществующего заказа. Второе: обновите модуль modxSmarty, если старый стоит. С большой долей вероятности должно помочь. Один из минусов процессоров в том, что у них нет собственного объекта обработки ошибок, они используют объект $modx->error, и если один процессор вернул ошибку и не выполнить сброс ошибок $modx->error->reset(), то все последующие процессоры на этой странице вернут ошибку. В новой версии modxSmarty добавлен сброс ошибок в смарти плагине processor. Третье: в любом случае, скорее всего у вас старая версия сборки. Если обновить ее проблематично для вас и с этой ошибкой не справитесь, напишите мне доступы в админку сайта на почту n.lanets@modxclub.ru, я поправлю.

[2015-02-09 21:40:50] (ERROR @ /index.php) Array ( [0] => Не был указан ID заказа ) при обновлении корзины после очищения на всех сайтах эта надпись только в корзине, а у меня еще в левом верхнем углу вот это вывод

Отступы бы строчкам в каталоге добавить, а то товары слипаются.

Даже сильно устаревшему сайту можно дать второе рождение. nhcom.ru — как раз такой случай. Изначально весь сайт был выполнен на статических файлах. Естественно, что управление таким сайтом затруднительно и требует участия специалиста. После того как в MODX-Клуб обратились владельцы nhcom.ru, сайт преобразился. Сделаем краткий обзор произошедших изменений. Во-первых, сайт был перенесен на движок ShopModxBox. Далее дело было за дизайном и отображением информации. Для оформления кнопок, всплывающих модальных окон и других элементов мы выбрали Bootstrap. Ниже представлены изображения главной страницы сайта до и после работ. Главная страница До После ?? После мы приступили к каталогу товаров. В каталоге не была предусмотрена возможность добавления товара в корзину да и самой корзины как таковой не было. Страница была перегружена текстовой информацией и плохо воспринималась. Сейчас посетители сайта могут сразу выбрать товар для покупки или купить товар в один клик. Кстати, мы думаем включить в нашу сборку магазина возможность заказа товара в один клик. Каталог товаров До После ?? А как раньше происходила покупка товара на сайте? Кликнув по ссылке “Купить”, вы попадали на страницу заказа. Дело оставалось за малым — набить нужное количество выбранного товара и заполнить форму с контактными данными. Теперь же все намного проще. Оформление заказа (корзина) До После ?? Так же изменилась и персональная страница товара. Кнопки “В корзину” и “Купить в один клик” и цена товара располагаются сверху, а не внизу страницы как раньше, что добавляет сайту юзабилити. Страница товара До После ?? На днях мы выполнили обновление компонентов магазина до последней версии, что еще добавило управляемости проекту, а так же существенно увеличило скорость загрузки сайта. Посмотреть и сравнить старый и новый магазин, можно перейдя по ссылкам.