Fi1osof 10 февраля 2015 8 36
Про процессоры у нас написано много, но вот этот комментарий натолкнул меня на мысль, что некоторые вообще не представляют себе что такое MODX-процессоры и с чем их едят. Учитывая то, что практически все наши разработки (в том числе и сборка ShopModxBox) основываются на работе процессоров, я решил написать эту статью, в которой постараюсь максимально подробно раскрыть тему процессоров. Если вы не понимаете процессоров, у вас никогда не получится нормально тюнинговать сборку ShopModxBox под себя, так что советую максимально четко изучить данный материал (для этого будет приведено множество примеров). Обязательно попробуйте выполнить представленные примеры самостоятельно и понять как они работают. Освоите — многое для вас станет понятней и проще.

Сразу скажу, что понимание принципов ООП сильно поможет вам в освоении этого материала, так что если у кого пока нет знаний в php-ООП, советую к изучению вот эту страничку.

Для начала выполним простейший скрипт (здесь и далее скрипты выполнять будем в компоненте Console в админке ShopModxBox (чтобы точно все примеры работали)).

<?php
print '<pre>';
ini_set('display_errors', 1);
$modx->switchContext('web');

$action = 'web/catalog/products/getdata';
$ns = 'modxsite';
$params = array(
    "limit" => 6,
);

if(!$response = $modx->runProcessor($action,
    $params
, array(
    'processors_path' => $modx->getObject('modNamespace', $ns)->getCorePath().'processors/',
))){
    print "Не удалось выполнить процессор";
    return;
}
 
print_r($response->getResponse()); 

В ответ мы получим примерно такой ответ:
Array
(
    [success] => 1
    [message] => 
    [count] => 6
    [total] => 6
    [limit] => 6
    [page] => 0
    [object] => Array
        (
            [134] => Array
                (
                    [id] => 134
                    [type] => document
                    [contentType] => text/html
                    [pagetitle] => Toshiba Satellite C50-A-K7K 15,6"
                    [longtitle] => .......................


Наблюдательные читатели могли заметить схожесть параметров в примере с вызовом процессора в смарти, а именно:
$action = 'web/catalog/products/getdata';
$ns = 'modxsite';
$params = array(
    "limit" => 6,
);
и
{processor action="web/catalog/products/getdata" ns="modxsite" params="limit=`6`" assign=result}

И это не случайно. Фактически, вызывая процессор в Смарти, мы вызываем представленный код, а точнее метод $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 записей.

А теперь попробуйте в своем Смарти-шаблоне прописать такой код:
{processor action="web/catalog/products/getdata" ns="modxsite" params="limit=`6`" assign=result}
<pre>
    {print_r($result, true)}
</pre>

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

Давайте рассмотрим данные ответа.

Сначала стандартные элементы:
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() Проверяет доступ к вызываемому процессору. К примеру, в нем можно прописать так:
public function checkPermissions() {
    return $this->modx->user->id && parent::checkPermissions();
}

Таким образом только если пользователь будет авторизован (объект пользователя $modx->user содержит значение id), а так же родительский процессор вернет успех на проверку checkPermissions(), тогда только будет возвращено true, и значит процессор может выполняться. Иначе будет возвращено Access denied.

initialize(). В этом методе как правило прописывается проверка передаваемых данных, установка дефолтовых значений и т.п. В нем позволительно возвращать не только true|false, но и просто текстовое сообщение. Данное сообщение будет так же расцениваться как ошибка и будет содержаться в параметре message описанного выше формата ответа. Рассмотрим довольно типичный код:
public function initialize(){
    $this->setDefaultProperties(array(
        "subject"    => "Заказ звонка с сайта",
    ));
    
    if(!$this->getProperty('name')){
        $this->addFieldError('name', 'Не указано имя');
    }

    if(!$this->getProperty('phone')){
        $this->addFieldError('phone', 'Не указан телефон');
    }

    // Если есть ошибки, возвращаем ошибку с сообщением
    if($this->hasErrors()){
        return "Не все обязательные поля заполнены";
    }
 
    return parent::initiaize();
}

Здесь мы установили значение по умолчанию «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(). Это самый главный метод процессора, который задает общую логику работы процессора, выполняя основные его методы в нужном порядке и обрабатывая ошибки. Давайте рассмотрим его код внимательней с комментариями.
public function run() {
        // Проверяем права на выполнение
        if (!$this->checkPermissions()) {
            // Если прав нет, ответ будет содержать ошибку доступа
            $o = $this->failure($this->modx->lexicon('permission_denied'));
        // Права есть, выполняем основной код
        } else {
            // Получаем массив словарей, если указан
            $topics = $this->getLanguageTopics();
            foreach ($topics as $topic) {
                // Подгружаем словари
                $this->modx->lexicon->load($topic);
            }
            // Выполняем инициализацию процессора
            $initialized = $this->initialize();
            // Если инициализация не вернула четко истину, 
            // то ответ будет содержать ошибку
            if ($initialized !== true) {
                $o = $this->failure($initialized);
            // иначе успех
            } else {
                $o = $this->process();
            }
        }
        // Получаем объект ответа процессора
        $response = new modProcessorResponse($this->modx,$o);
        // Возвращаем ответ
        return $response;
    }
Этот базовый метод задает стандарт выполнения любого 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






Вообще, если освоить навигацию по тому ресурсу и хорошенько покопаться, можно много всего найти и узнать. К примеру, можно ощутить, что MODX со своими процессорами — это целая вселенная!


Здесь видна лишь маленькая часть имеющихся в 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

<?php
print '<pre>';
ini_set('display_errors', 1);
$modx->switchContext('web');
$modx->setLogTarget('HTML');

$action = 'resource/create';
$ns = '';
$params = array(
    "pagetitle" => "New document",
    "content"   => "some content", 
);

if(!$response = $modx->runProcessor($action,
    $params
, array(
    'processors_path' => $ns ? $modx->getObject('modNamespace', $ns)->getCorePath().'processors/' : null,
))){
    print "Не удалось выполнить процессор";
    return;
}
 
print_r($response->getResponse()); 


Выполним его. Если у вас все хорошо выполнилось, вы получите ответ, содержащий success => 1 и id созданного документ, типа такого:
Array
(
    [success] => 1
    [message] => 
    [total] => 0
    [errors] => Array
        (
        )

    [object] => Array
        (
            [id] => 155
        )
)

Иначе будет ошибка, например такая:
Array
(
    [success] => 
    [message] => 
    [total] => 2
    [errors] => Array
        (
            [0] => Array
                (
                    [id] => uri
                    [msg] => Ресурс с идентификатором 155 уже использует URI new-document.html. Пожалуйста, введите уникальный псевдоним или используйте «Заморозить URI», чтобы вручную заменить его.
                )

            [1] => Array
                (
                    [id] => alias
                    [msg] => Ресурс с идентификатором 155 уже использует URI new-document.html. Пожалуйста, введите уникальный псевдоним или используйте «Заморозить URI», чтобы вручную заменить его.
                )

        )

    [object] => Array
        (
        )

)


2. Обновим существующий документ.
Для этого нам надо будет вызвать процессор resource/update. Единственно, в случае с обновлением документа не достаточно будет передать только его id-шник и изменяемые поля, так как в процессоре часть кода основывается на передаваемых данных, а не на данных полученного объекта документа, но это не беда.
<?php
print '<pre>';
ini_set('display_errors', 1);
$modx->switchContext('web');
$modx->setLogTarget('HTML');

$action = 'resource/update';
$ns = '';
$doc_id = 1;
$params = array_merge($modx->getObject('modResource', $doc_id)->toArray(), array(
    "pagetitle" => "New pagetitle",
));

if(!$response = $modx->runProcessor($action,
    $params
, array(
    'processors_path' => $ns ? $modx->getObject('modNamespace', $ns)->getCorePath().'processors/' : null,
))){
    print "Не удалось выполнить процессор";
    return;
}
 
print_r($response->getResponse()); 


3. Получим данные товаров в каталоге
Для этого вызовем процессор web/catalog/products/getdata самой сборки ShopModxBox.
<?php
print '<pre>';
ini_set('display_errors', 1);
$modx->switchContext('web');
$modx->setLogTarget('HTML');

$action = 'web/catalog/products/getdata';
$ns = 'modxsite'; 
$params = array(
    "limit"    => 3,
    "sort"      => "createdon",
    "dir"       => "desc",
);

if(!$response = $modx->runProcessor($action,
    $params
, array(
    'processors_path' => $ns ? $modx->getObject('modNamespace', $ns)->getCorePath().'processors/' : null,
))){
    print "Не удалось выполнить процессор";
    return;
}
 
print_r($response->getResponse()); 


В ответ вы должны получить массив данных товаров (максимум трех по условию запроса), отсортированных по дате создания в обратном порядке.

На этом на сегодня все. А в качестве домашнего задания попробуйте проследить всю родословную процессора из последнего примера, и понять как формируется запрос на получение данных и какие таблицы он затрагивает.
36 комментариев
Tramp13571
Tramp1357 10 февраля 2015г в 11:57 #
Как-то делал, может, пригодятся:


Fi1osof1
Fi1osof 10 февраля 2015г в 15:55 #
Отличная схемка :)
Tramp13571
Tramp1357 10 февраля 2015г в 15:56 #
Спасибо :)
Это я на заре постижения работы с процессорами делал, чтобы вникнуть лучше
Fi1osof1
Fi1osof 10 февраля 2015г в 16:06 #
Ну ты-то давно уже вник))
Tramp13571
Tramp1357 10 февраля 2015г в 16:06 #
Может, кому-то еще поможет лучше с логикой разобраться
m
magr0s 11 февраля 2015г в 10:00 #
Про процессоры у нас написано много ...
Николай, написано много и не только у вас но много не означает толково, но ЭТОТ материал лично для меня огромной ценности.

Большое спасибо за данный материал.

п.с. кстати у вас подача замечательная, читается на ура
Fi1osof1
Fi1osof 11 февраля 2015г в 18:49 #
Пожалуйста! :)
6 часов писал…
stager1
stager 11 февраля 2015г в 11:15 #
В самом начале примера правильнее будет print_r взять в фигурные скобки. вот так
{print_r($result)}
Fi1osof1
Fi1osof 11 февраля 2015г в 18:43 #
Fixed.
спасибо!
m
magr0s 13 февраля 2015г в 19:06 #
Cпрошу наверное я вот что )
Скажу сразу тема понятное дело облизана в интернете со всех сторон, но нет ничего лучше примера кода поэтому я нашел на Гите modHybridAuth и начал изучать.

и мне интересно вот что.
есть connector.php который запускает процессор 'profile/auth'.
Этот процессор расширяет процессор modProcessor.

У меня вроде все и получилось но вот ответ приходит имя процессора, я подозреваю что проблема доступом к modProcessor. Что не так?

Код один в один за исключением названия пакета)


class myTestProcessor extends modProcessor {
    public function process(){
        return 'done';
    }
}
return 'myTestProcessor';


Fi1osof1
Fi1osof 13 февраля 2015г в 19:22 #
1. Давайте полный листинг вызываемого кода, включая коннектор (даже если все повторяется). Мы не можем гадать измененный у вас код или нет (выкладывайте куда-нибудь на гист или типа того).
2. Приведите четко сообщение ошибки. Копипаст, плиз, а не «дословный перевод».
3. На сам modProcessor никаких прав не надо. Ошибка — это не всегда отсутствие доступа.
m
magr0s 13 февраля 2015г в 19:49 #
Gist

ошибки нет. в логах все чисто. я получаю статус 200 и в ответ название процессора, а не worked

п.с. чистый копипаст названия такие потому что просто разбираюсь, если вы об этом

Fi1osof1
Fi1osof 13 февраля 2015г в 20:04 #
Потому что название файла должно быть test.class.php, а не test.php
Просто test.php — это устаревший флэт-процессор, он возвращает то, что у него в return. А у вас return 'myTestProcessor';
m
magr0s 13 февраля 2015г в 20:07 #
спасибо, то что надо…
значить все файлы процессоров должны быть *.class.php
Fi1osof1
Fi1osof 13 февраля 2015г в 20:24 #
Да, должны быть. Не просто же так у нас процессоры getdata.class.php, getlist.class.php и т.п.
m
magr0s 18 февраля 2015г в 17:47 #
Здравствуйте.
В процессе разбора процессоров возникла проблема с которой не могу справится.
Работаем с пользователями нужно выдернуть с базы всех пользователей у которых sudo = 0. Результатом работы процессора должна быть выборка в которой указан только username
С критерием для выбора разобрался а вот с тем что нужно вернуть не могу.

Процессор отдает полностью все поля таблицы. А нужно например только id и username.
Tramp13571
Tramp1357 18 февраля 2015г в 18:18 #
В процессорах это метод setSelection,
$c->select(array(
    'id',
    'username',
));

Рекомендую почитать документацию по xPDO, поскольку процессоры общаются с базой через нее.
m
magr0s 18 февраля 2015г в 18:41 #
поспешил отписаться…
все равно $response->getResponse() возвращает всю таблицу


class xtestGetUsersProcessor extends modObjectGetListProcessor{
    public $classKey = 'modUser';
    public $defaultSortField = 'id';
    public $defaultSortDirection = 'ASC';
    public $objectType = 'modUser';

    public function prepareQueryBeforeCount(xPDOQuery $c) {
            $c->where(array(
                'sudo' => 1,
            ));
        return $c;
    }
    public function setSelection(xPDOQuery $c){
        $c->select(array(
            'id',
            'username',
        ));
        return $c;
    }
}
return 'xtestGetUsersProcessor';
Tramp13571
Tramp1357 18 февраля 2015г в 18:47 #
А, вот что :)
Я по инерции подумал, что из modxSite процессор за основу взят
Если посмотреть core/model/modx/modprocessor.class.php, то у процессора modObjectGetListProcessor нет метода
setSelection. Здесь select можно разместить как раз в функции prepareQueryBeforeCount
m
magr0s 18 февраля 2015г в 18:53 #
ага)я тоже уже пересмотрел…

дело в том что даже если вот так


public function prepareQueryBeforeCount(xPDOQuery $c) {
            $c->where(array(
                'sudo' => 1,
            ));

        $c->select(array(
            'id',
            'username',
        ));

        return $c;
    }

$response->getResponse() выдает полностью все колонки таблицы

что ж за беда то такая
proxyfabio1
proxyfabio 18 февраля 2015г в 18:59 #
Надо в метод `outputArray` смотреть
Tramp13571
Tramp1357 18 февраля 2015г в 19:05 #
Можно и там, но лучше все-таки сформировать запрос без лишних полей. Нагрузка на мускул меньше будет.
proxyfabio1
proxyfabio 18 февраля 2015г в 19:33 #
Так с этим никто не спорит. Я как бы намекнул, что надо таки туда поглядеть и увидеть, что базовый гетлист процессор в методе `getData` получает коллекцию, а в методе `iterate` дергается `prepareRow` с `$object->toArray()` под капотом; и `select` полетит, но не в результатах для вывода.
Просто лень все это было печатать…
Tramp13571
Tramp1357 18 февраля 2015г в 20:37 #
Ты наверно имел в виду метод process?
proxyfabio1
proxyfabio 18 февраля 2015г в 20:42 #
Нет, я имел в виду вот это
Tramp13571
Tramp1357 18 февраля 2015г в 20:47 #
Понял :)
Fi1osof1
Fi1osof 19 февраля 2015г в 08:41 #
Эх, не зря Серега мой лучший ученик :)))
Fi1osof1
Fi1osof 19 февраля 2015г в 08:40 #
Саш, смотри ниже. И статью смотри, указанную в комментарии. Таким образом ты только увеличишь нагрузку на сервер. Это такие особенности xPDO.
Tramp13571
Tramp1357 18 февраля 2015г в 19:03 #
можно вставить перед
return $c;

такие строки
$c->prepare();
print_r($c->toSQL());
die;


и если выполнить этот процессор в консоли, выведется sql-запрос
Fi1osof1
Fi1osof 19 февраля 2015г в 08:38 #
Потому что надо смотреть в сторону методе prepareRow(). Как мы разбирали предметно ранее, xPDOObject::fromArray() выдернет из базы данных все строки объекта, так что $this->object->select() определенных колонок ничего не изменит, но нагрузки на сервер добавит. Надо переопределить этот метод в своем процессоре и вернуть типа
return array(
    'id'    => $this->object->id,
    'username'    => $this->object->username,
);
m
magr0s 18 февраля 2015г в 18:31 #
Спасибо. постепенно дойдем и туда…

п.с. лирическое ощущение… modx совсем не такой каким я его себе представлял
proxyfabio1
proxyfabio 18 февраля 2015г в 19:00 #
А каким представлял?
m
magr0s 18 февраля 2015г в 19:14 #
я знал что есть такая штука как процессоры но это все было далеким и каким то туманным.
вся работа заключалась в сниппет->phpClass

начав знакомство с процессорами я вижу что при таких возможностях использование snippet->phpClass это просто кощунство

`outputArray`
т.е. формировать строку ответа в ручную?

proxyfabio1
proxyfabio 18 февраля 2015г в 19:33 #
Нет. Написал ответ выше
Tramp13571
Tramp1357 18 февраля 2015г в 20:42 #
я бы все-таки посоветовал действительно поставить пакет modxsite — там есть зороший набор уже проверенных процессоров. сот от того getlist лучше и наследовать.

Сам я глубоко особо не копал в системных процессорах, но конкретно в объявлении modObjectGetListProcessor
вызывает беспокойство слово abstract — это говорит, что он неполный (просто основа для дальнейшего развития), и какие-то необходимые для работы методы необходимо дописать.
m
magr0s 18 февраля 2015г в 21:58 #
я бы все-таки посоветовал действительно поставить пакет modxsite — там есть зороший набор уже проверенных процессоров.

это не боевой проект я никуда не спешу и раз уж так пошло дело с процессорами то думаю попытаться вникнуть до конца.
а modxSite установлен :)
m
magr0s 18 февраля 2015г в 23:34 #
ну то что я хотел я получил осталось узнать на сколько все это правильно сделано.


class xtestGetUsersProcessor extends modObjectGetListProcessor{
    public $classKey = 'modUser';
    public $defaultSortDirection = 'ASC';
    public $defaultSortField = 'username';

.........

    public function prepareRow(xPDOObject $object) {
        $objectArray = array();
        $objectArray['id'] = $object->get('id');
        $objectArray['username'] = $object->get('username');
        return $objectArray;
    }
}
return 'xtestGetUsersProcessor';

Tramp13571
Tramp1357 19 февраля 2015г в 01:15 #
это тоже будет работать, но при этом в запрос попадают все поля, и соответственно возрастает нагрузка и потребление памяти. Лучше все-таки до моментаобращения к мерверу правильно настроить select, чтобы в выборку попали только нужные поля.
Странной дело, пока не могу понять
public function prepareQueryBeforeCount(xPDOQuery $c){
        $c=parent::prepareQueryBeforeCount($c);
        $c->select(['modUser.id','modUser.username']);
$c->prepare();
print $c->toSQL();
die;
    }

возвращает правильный запрос:
SELECT modUser.id, modUser.username FROM `spmx_users` AS `modUser`

а на выходе действительно отдает все поля
Array
(
    [success] => 1
    [total] => 5
    [results] => Array
        (
            [0] => Array
                (
                    [id] => 1
                    [username] => admin
                    [password] => P3/gWbTaphPr//hV8djePt1qdcjN5CIxBVKE5tqdJyQ=
                    [cachepwd] => 
                    [class_key] => modUser
                    [active] => 1
                    [remote_key] => 
                    [remote_data] => 
                    [hash_class] => hashing.modPBKDF2
                    [salt] => b052eb5470a21ff53697a5bf4cb43649
                    [primary_group] => 1
                    [session_stale] => Array
                        (
                            [1] => spravochniki
                            [2] => web
                        )

                    [sudo] => 1
                )

            [1] => Array

нигде в коде больше $c->select(...) не встречается вплоть до $modx->getCollection. Ничего не понимаю.
Fi1osof1
Fi1osof 19 февраля 2015г в 08:45 #
Сегодня, Саша, мир твой рухнет))) Статью на этот счет почитать я тебе выше написал))
Tramp13571
Tramp1357 19 февраля 2015г в 09:34 #
Да уж, действительно :)
Я там еще не ковырял, не было необходимости :)
Век живи…
Fi1osof1
Fi1osof 19 февраля 2015г в 09:35 #
Вот как-то так)
Fi1osof1
Fi1osof 19 февраля 2015г в 08:44 #
Все правильно сделано. Единственное, просто по синтаксису, проще так:
return array(
    'id'    => $this->object->id,
    'username'    => $this->object->username,
);

m
magr0s 19 февраля 2015г в 01:26 #
я не знаю прав ли я
но так просто нельзя сделать…
коллекция получает значения с таблицы и забивает ими класс xPDO. т.е. на выходе мы принимает не массив, а объекты конкретного класса…
наверное

п.с. из консоли не выход getCollection с селектом
m
magr0s 19 февраля 2015г в 10:39 #
Как мы разбирали предметно ранее, xPDOObject::fromArray() выдернет из базы данных все строки объекта, так что $this->object->select() определенных колонок ничего не изменит, но нагрузки на сервер добавит
шикарно… хотел было сказать «ой, как жаль что статья не попала ко мне раньше» но я рад что разобрали все это методом проб и ошибок, а статья как раз в вовремя, объяснила все более точно и показала откуда ноги растут
Сегодня, Саша, мир твой рухнет)))
:) это все сейчас похоже на то как маленькие дети игрушку новую получать… «ВАУ, а тут и дверца еще открывается...»

Всем большое спасибо!!!
Fi1osof1
Fi1osof 19 февраля 2015г в 10:44 #
«ВАУ, а тут и дверца еще открывается...»
:)))
Пожалуйста!
Tramp13571
Tramp1357 19 февраля 2015г в 11:00 #
«ВАУ, а тут и дверца еще открывается...»
Ага, именно так :)
Жизнь периодически тыкает носом в статьи, которые видел раньше, но на том моем уровне они вызывали зевоту :)
Потом забывались. А зря… :)
Все чаще появляется навязчивая мысль перечитать весь modxclub.ru c нуля :)
Fi1osof1
Fi1osof 19 февраля 2015г в 11:05 #
Почитай:) Тут много чего интересного есть. Одна из наиболее интересных статей вот эта. Если все поймешь, что там написано (и всю сопутствующую информацию), то тогда у тебя не будет больше вопросов «а что же сайт так тормозит?», то есть будешь управлять кешированием и производительностью как положено.
Tramp13571
Tramp1357 19 февраля 2015г в 11:07 #
Спасибо за наводку, обязательно изучу. Теперь уровень чуть выше :)
Fi1osof1
Fi1osof 19 февраля 2015г в 11:09 #
Не за что!
Tramp13571
Tramp1357 19 февраля 2015г в 11:04 #
А все-таки странное поведение xPDO… Хотя не глупые люди писали, зачем-то надо :)
Коля, а почему тогда в твоих процессорах работает конструкция $c->select(...)? Потому, что не урезаешь, а расширяешь?
Fi1osof1
Fi1osof 19 февраля 2015г в 11:08 #
Коля, а почему тогда в твоих процессорах работает конструкция $c->select(...)?
Потому что по умолчанию вообще метод xPDOQuery::select() не выполняется. Попробуй вот так сделать:
$q = $modx->newQuery('modResource');
$q->prepare();
print $q->toSQL();
Увидишь все колонки запрошенного класса, при чем алиасные, а не как они есть в БД.
Tramp13571
Tramp1357 19 февраля 2015г в 11:14 #
Это я понял. Не пойму, зачем? Нелогично как-то, на мой взгляд. Если мне надо только name, так и дай мне только его :)
Хотя, как говорил, видимо было какие-то обоснования. Возможно, выбратьб все поля проще, чем отфильтровывать при формировании выборки…
По универу помню только, что нам вбивали, что надо максимально запрос оптимизировать, сократить нагрузку на сервер. Может, и поменалась политика партии с тех времен :)
Fi1osof1
Fi1osof 19 февраля 2015г в 12:32 #
Сделай так:
$q = $modx->newQuery('modUser');
$s = $q->prepare();
$s->execute();

print_r($s->fetch(2));

Получишь результат типа
Array
(
    [modUser_id] => 2
    [modUser_username] => xxxx
    [modUser_password] => xxxxxx
    [modUser_cachepwd] => 
    [modUser_class_key] => modUser
    [modUser_active] => 1
    [modUser_remote_key] => 
    [modUser_remote_data] => 
    [modUser_hash_class] => hashing.modPBKDF2
    [modUser_salt] => 
    [modUser_primary_group] => 22
    [modUser_session_stale] => 
    [modUser_sudo] => 1
)


И сделай так:
$q = $modx->newQuery('modUser');
$q->select(array(
    "{$q->getAlias()}.*",
));
$s = $q->prepare();
$s->execute();

print_r($s->fetch(2));


Результат:
Array
(
    [id] => 2
    [username] => xxxxxx
    [password] => xxxxxxx
    [cachepwd] => 
    [class_key] => modUser
    [active] => 1
    [remote_key] => 
    [remote_data] => 
    [hash_class] => hashing.modPBKDF2
    [salt] => 
    [primary_group] => 22
    [session_stale] => 
    [sudo] => 1
)


Найди 10 отличий.
Tramp13571
Tramp1357 19 февраля 2015г в 12:49 #
Да… Надо чаще встречаться :)
Не было пока необходимости, не вдавался в такие подробности, вполне хватает пока твоих getdata, при необходимости от них и наследую… Все. Выберу время в ближайшее время и займусь шпатлеванием пробелов :)
Fi1osof1
Fi1osof 19 февраля 2015г в 12:55 #
Давай. Не будет лишним.
m
magr0s 19 февраля 2015г в 12:18 #
да не «выбирает» оно поля… создаются объекты… хотя опять таки выбирает но для того что бы создать объекты :)
m
magr0s 20 февраля 2015г в 16:58 #
усвоил.
не скажу что все прям понятно, родилось еще больше вопросов, но об этом потом…

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

процессор getusers.class.php

require_once (dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))).'/model/modx/processors/security/user/getlist.class.php');

class xtestGetUserListProcessor extends modUserGetListProcessor{
    public function prepareQueryBeforeCount(xPDOQuery $c) {
        $c->leftJoin('modUserProfile','Profile');
        $c->leftJoin('modUserGroupMember', 'UserGroupMembers');
        $c->leftJoin('modUserGroup', 'UserGroup', 'UserGroupMembers.user_group = UserGroup.id');
        return $c;
    }

    public function prepareQueryAfterCount(xPDOQuery $c) {
        $c->select($this->modx->getSelectColumns('modUser','modUser'));
        $c->select($this->modx->getSelectColumns('modUserProfile','Profile','',array('fullname','email','blocked')));
        $c->select($this->modx->getSelectColumns('modUserGroup','UserGroup','',array('name')));
        return $c;
    }

    public function prepareRow(xPDOObject $object){
        return array(
            'id' => $object->id,
            'username' => $object->username,
            'fullname' => $object->fullname,
            'email' => $object->email,
            'group' => $object->name,
        );
    }
}
return 'xtestGetUserListProcessor';    

ну и вот такой результат

уже при написании поста возник вопрос а что если у пользователя 2 группы… отложил пост полез пробовать…
если у пользователя 2 группы то покажет последнюю
а если сделать вот так

$c->select($this->modx->getSelectColumns('modUser','modUser', '', array('id', 'username')));

то в таблице будет 2 строки с разными группами…

а еще есть такая штука как права… для выполнения вышеуказанного процессора пользователь должен иметь разрешения 'view_user'.

нравится «зараза».
Fi1osof1
Fi1osof 20 февраля 2015г в 19:13 #
Вот это: require_once (dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))).'/model/modx/processors/security/user/getlist.class.php');
замените на require_once MODX_PROCESSORS_PATH.'security/user/getlist.class.php');

Про дубляжи записей: изучайте SQL, там вопрос только в нем, а не в MODX-е. Какой SQL-запрос будет сформирован, такие данные и будут получены.
m
magr0s 22 февраля 2015г в 22:35 #
Вечер добрый.

Николай, а скажите пожалуйста, почему Вы для получения объекта используете *GetListProcessor, с чем это связано?

и вот есть такая беда есть:

*.tpl

    {processor action='web/groups/getgroup' ns='xtest' assign=result}
    {$result|@var_dump} // показывает NULL


require_once dirname(dirname(__FILE__)) . '/getobject.class.php';

class xtestGetGroupProcessor extends xtestGetObjectProcessor{
    public $classKey = 'modUserGroup';
    // ну даже так
    public function initialize(){
        $this->setProperty('id', 2);

        return parent::initialize();
    }

}
return 'xtestGetGroupProcessor';

class xtestGetObjectProcessor extends modObjectGetProcessor {

    public $checkViewPermission = false;

} 
return 'xtestGetObjectProcessor';

Через ajax->connector или консоль процессор возвращает то что от него хотят
m
magr0s 23 февраля 2015г в 00:22 #
корень зла определен.
сразу как то не обратил внимание потому что smarty работало.

при обращении к странице по ajax smarty то работает но вот modxSmarty неа…
Николай писал об этом, решение нашлось в статье о каталоге :)
Fi1osof1
Fi1osof 23 февраля 2015г в 10:58 #
Я об этом не раз писал, и именно поэтому и писал статью про правильный Ajax-каталог. Конечно может и есть более правильная его организация, но на мой взгляд Ajax на документах правильней делать, если нужно HTML получить. А если просто данные нужны, тогда через коннекторы.
m
magr0s 24 февраля 2015г в 23:15 #
Подскажите пожалуйта, а как правильно работать с процессором modUserCreateProcessor
На сколько я понимаю там вся проблема в параметрах.
для начала через форму скармливаю 4 поля
username, email, password, password_confirm

процессору дополнительно передается параметр passwordnotifymethod = 's'

В итоге я получаю пользователя но система сама ему генерирует пароль, т.е. на сколько я понимаю поля пароля и дубля пароля игнорируются и даже не проверяются.
Найти в инете хоть какое то описание как работает этот процессор у меня не получилось(
m
magr0s 25 февраля 2015г в 00:05 #
нашел в коде админки :)

$this->setDefaultProperties(
            array(
                'active' => true,
                'passwordnotifymethod' => 'w', // e - слать на mail, s - уведомление в окно, w- мне просто не надо ни то ни то
                'passwordgenmethod' => 'spec', // ручной ввод пароля
                'specifiedpassword' => $this->getProperty('password'),
                'confirmpassword' => $this->getProperty('password_confirm'),
            )
        );
Fi1osof1
Fi1osof 25 февраля 2015г в 07:09 #
Вот, достаточно просто почитать процессор и его валидатор :)
M
MisterN 07 апреля 2015г в 15:44 #
За fossies.org спасибо отдельное. За статьи у вас наверное спасибки — девать некуда, в отличии от донейтов.
Вот решил попробовать получить данные из своего процессора, а не с помощью newQuery, а из своего процессора. Скинул modExtra, накидал схему, переименовал все, как мне надо, в общем запустил стандартно CMP — работает отлично. Ну что, создал в папке с процессорами рядом с mgr /processors/web/ В папку web скопировал процессор, который наследует modObjectGetListProcessor. Буквально указываю первые пять параметров (класские и что сортировать по id-DESC), и закрываю скобку. В принципе, почти такой же с совсем малой правкой в CMP благополучно данные кидает. Пишу в создаваемом процессоре require_once с классом данной таблицы. Абсолютно обычный класс, который extends xPDOSimpleObject
Вызываю в консоле, как в начале статьи вы другие процессоры дергаете — приходит ошибки Could not get table class for class и {«success»:true,«total»:«0»,«results»:[]}
Ставлю в консоли $modx->addPackage('booking', MODX_BASE_PATH.'path/path/model/'); — ноль внимания, фунт призрения.
Добавляю $d= $modx->getObject('myClass',1); — работает!
Я понимаю, что можно прописать загрузку модели в настройках extension_packages, но если я хочу все же, чтобы модель загружалась только там, где я дергаю данные, а на остальных страницах небыло? Как вообще пакеты по-грамотному подгружать к процессорам?
m
magr0s 07 апреля 2015г в 16:06 #
namespace не?
M
MisterN 07 апреля 2015г в 16:17 #
С неймспейсом все ок. $modx->getObject('modNamespace', $ns)->getCorePath(); выдает правильный путь.
M
MisterN 07 апреля 2015г в 16:20 #
Да и если require в процессоре не верно прописан — ошибка об этом вылезает.
Fi1osof1
Fi1osof 08 апреля 2015г в 00:58 #
спасибки — девать некуда, в отличии от донейтов.
Это уж точно :)

Добавляю $d= $modx->getObject('myClass',1); — работает!
Куда именно добавляешь? И выложи полный листинг твоего процессора и скрин файловой структуры твоего компонента, чтобы было видно что и как у тебя там лежит.
M
MisterN 08 апреля 2015г в 11:28 #
Куда именно добавляешь?

Да в консоль же добавляю.
Отписал отдельную тему modxclub.ru/topics/vyizov-svoego-proczessora-1632.html
Авторизуйтесь или зарегистрируйтесь (можно через соцсети ), чтобы оставлять комментарии.