Полезные выборки xPDO

Задают много вопросов "а как выбрать бла-бла-бла и вывести все id товаров по нему?", на которые регулярно пишу в комментариях разные выборки на xPDO.
Пришло время собрать их в кучу, чтобы было проще искать.

Вывод количества товаров в категории

Сниппет получает сумму всех товаров в категории, с учетом мультикатегорий MS2. Можно указать нужную категорию параметром $parent.
<?php
if (empty($parent)) {$parent = $modx->resource->id;}
$pids = array_merge(array($parent), $modx->getChildIds($parent));
$ids = array();

$q = $modx->newQuery('msProduct');
$q->where(array('class_key' => 'msProduct','parent:IN' => $pids,'published' => 1,'deleted' => 0));
$q->select('`msProduct`.`id`');
if ($q->prepare() && $q->stmt->execute()) {
    $ids = $q->stmt->fetchAll(PDO::FETCH_COLUMN);
}

$q = $modx->newQuery('msProduct');
$q->leftJoin('msCategoryMember', 'Member', '`Member`.`product_id` = `msProduct`.`id`');
$q->where(array('class_key' => 'msProduct','Member.category_id:IN' => $pids,'published' => 1,'deleted' => 0));
$q->select('`msProduct`.`id`');
if ($q->prepare() && $q->stmt->execute()) {
    $ids2 = $q->stmt->fetchAll(PDO::FETCH_COLUMN);
    if (!empty($ids2)) {
        $ids = array_unique(array_merge($ids, $ids2));
    }
}

return count($ids);

Вывод производителей товаров MS2

Сниппет получает всех производителей, у которых есть хоть один активный товар и выводит в виде селектбокса.
<?php
$q = $modx->newQuery('msVendor');
$q->innerJoin('msProductData', 'msProductData', '`msProductData`.`vendor` = `msVendor`.`id`');
$q->innerJoin('msProduct', 'msProduct', array(
    '`msProductData`.`id` = `msProduct`.`id`',
    'msProduct.deleted' => 0,
    'msProduct.published' => 1
));
$q->groupby('msVendor.id');
$q->sortby('name','ASC');
$q->select(array('msVendor.id', 'name'));
$options = '<option value="0">Нет</option>';
if ($q->prepare() && $q->stmt->execute()) {
    while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        $options .= '<option value="'.$row['id'].'">'.$row['name'].'</option>';
    }
}
return '<select name="vendors">'.$options.'</select>';

Получение id товаров по опции MS2

Сниппет выбирает все значения указанной опции (теги, цвета) и печатает массив id товаров, у которых оно есть.
$key = 'tags'; // имя опции товара
$category = 0; // фильтрация по категории

$q = $modx->newQuery('msProductOption');
$q->innerJoin('msProduct', 'msProduct', 'msProduct.id=msProductOption.product_id');
$q->sortby('msProductOption.value','ASC');
$q->select('DISTINCT(msProductOption.value), msProduct.id');
$q->where(array('msProductOption.key' => $key));
if (!empty($category)) {
    $ids = $modx->getChildIds($category);
    $ids[] = $category;
    $q->innerJoin('msCategory', 'msCategory', 'msCategory.id=msProduct.parent');
    $q->where(array('msCategory.id:IN' => $ids));
}
$result = array();
if ($q->prepare() && $q->stmt->execute()) {
    while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        $result[$row['value']][] = $row['id'];
    }
}
echo '<pre>';print_r($result); die;

Дерево ресурсов

Сниппет, который строит дерево ресурсов глубиной в 2 уровня, от указанного родителя.
$parent = 3;
$exclude_parents = array(100500,123456);
$template = 4;

$ids = $modx->getChildIds($parent));
$q = $modx->newQuery('modResource', array('parent:IN' => $ids, 'OR:id:IN' => $ids));
$q->andCondition(array('id:NOT IN' => $exclude_parents, 'template' => $template));
$q->select('id,pagetitle,parent');
$resources = array();
if ($q->prepare() && $q->stmt->execute()) {
    while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        if ($row['parent'] == $parent) {
            if (isset($resources[$row['id']])) {
                $resources[$row['id']] = array_merge($resources[$row['id']], $row);
            }
            else {
                $resources[$row['id']] = $row;
                $resources[$row['id']]['children'] = array();
            }
        }
        else {
            $resources[$row['parent']]['children'][$row['id']] = $row;
        }
    }
}
echo'<pre>';print_r($resources);die;

Вывод всех категорий товара

Сниппет выбирает и выводит все категории, к которым принадлежит товар - его можно использовать как своеобразные теги. Предложил Виктор Долгий.
<?php
if (empty($rid)) {$rid = $modx->resource->id;}
if (empty($pid)) {$pid = $modx->resource->parent;}
if (empty($delimeter)) {$delimeter = ' , ';}
$scheme = $modx->getOption('link_tag_scheme', null, 'full', true);

$q = $modx->newQuery('msCategory');
$q->leftJoin('msCategoryMember', 'msCategoryMember', array(
    '`msCategory`.`id` = `msCategoryMember`.`category_id`'
));
$q->sortby('pagetitle','ASC');
$q->groupby('id');
$q->select(array('id','pagetitle'));
$q->where('`msCategoryMember`.`product_id` = '.$rid.' OR `id` = '.$pid);

$result = array();
if ($q->prepare() && $q->stmt->execute()) {
    while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        $url = $modx->makeUrl($row['id'], '', '', $scheme);
        $result[] = '<a href="'.$url.'">'.$row['pagetitle'].'</a>';
    }
}
return implode($delimeter, $result);

Выбор товаров по опциям

Указываете имя опции и родителя, откуда искать.
<?php
$key = 'tags'; // имя опции товара
$category = 0; // фильтрация по категории
$param1 = 'имя тега';

$q = $modx->newQuery('msProductOption');
$q->innerJoin('msProduct', 'msProduct', 'msProduct.id=msProductOption.product_id');
$q->where(array('msProductOption.key' => $key, 'msProductOption.value'=> $param1));
$q->sortby('msProductOption.value','ASC');
$q->select('DISTINCT(msProductOption.value), msProduct.id');
$q->where(array('msProductOption.key' => $key));
if (!empty($category)) {
    $ids = $modx->getChildIds($category);
    $ids[] = $category;
    $q->innerJoin('msCategory', 'msCategory', 'msCategory.id=msProduct.parent');
    $q->where(array('msCategory.id:IN' => $ids));
}
$result = array();
if ($q->prepare() && $q->stmt->execute()) {
    while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        $res['id'][] = $row['id'];
    }
    $result = implode(",", array_unique($res['id'])) ;

}
print_r($result);

Вывод ссылок на дополнительные категории товара

Выборка дополнительных категорий и вывод ссылок на них.
<?php
if (empty($id)) {$id = $modx->resource->id;}
if (empty($tpl)) {$tpl = '@INLINE <a href="[[~[[+id]]]]">[[+pagetitle]]</a>';}
$pdo = $modx->getService('pdoFetch');

$conditions = array('product_id' => $id);
$options = array(
    'innerJoin' => array(
        'msCategory' => array('on' => 'msCategoryMember.category_id = msCategory.id')
    ),
    'select' => array('msCategory' => 'all'),
    'sortby' => 'msCategory.id'
);
$rows = $pdo->getCollection('msCategoryMember', $conditions, $options);

$output = '';
foreach ($rows as $row) {
    $output .= $pdo->getChunk($tpl, $row);
}

return $output;
Нужна свежая версия pdoTools.

Заключение

Если было еще - присылайте линки в комментах, будем дополнять.

21 комментарий

Alex Vakhitov
Люблю ORM modx все просто и понятно, в действительности выходит что просто пишешь текс с пожеланиями что хочешь получить (:
Виталий Батушев
Но почему без pdoTools?!
Василий Наумкин
А зачем оно тут? Нет ни сложного построения запроса, ни дополнительных выборок, ни рендера чанков.
Это просто чистые выборки, которые можно интегрировать куда угодно, в том числе и в pdoTools-сниппет.
Не выводит цвета вторая выборка. У меня поле цвет лежит в таблице modx_ms2_products (преобразованное в строку), в параметрах сделал так:
$key = 'color'; // имя опции товара
$category = $modx->resource->get('id'); // фильтрация по категории
в результате
Array ( ) 
но цвета есть. Если не задавать категорию - то все работает, все цвета возвращает. А если задать - то нет.
Написал оч простой код для получения уникальных цветов в категории товаров:
<?php
$children_ids = implode(',', $modx->getChildIds($modx->resource->get('id')));
$q = $modx->query('SELECT DISTINCT(color) FROM modx_ms2_products WHERE id in('.$children_ids.')');
if (is_object($q)) {
    $result = array();
    while ($row = $q->fetch(PDO::FETCH_ASSOC)) {
        $result[] = $row['color'];
    }
    print_r($result);
}
Василий Наумкин
Естественно.
Ты же изменил поле, и теперь оно хранит не массив (который копируется в таблицу опций), а строку. То, что ты получаешь без указания категории - это старые записи, скорее всего, уже не актуальные.
Так что да, тебе нужна простейшая выборка одного поля из одной таблицы, как ты и сделал.
Саша Иващенко
А как в "Вывод производителей товаров MS2" выводить ссылку на товары этого производителя? Я немного под себя переделал ваш код и немного задумался а как добавить еще ссылку на продукцию по этому производителю? Тут у меня уже выводятся - id name logo и description (сниппет fabriki):
<?php
$category = 0;    // id категории для начала выборки
$depth = 10;    // глубина выборки по дочерним категориям
$limit = 200;   // ограничение результатов

$q = $modx->newQuery('msProduct');
$q->innerJoin('msProductData', 'Data', 'msProduct.id = Data.id');
$q->innerJoin('msVendor', 'Vendor', 'Data.vendor = Vendor.id');
$q->leftJoin('msCategoryMember', 'Member', 'Member.product_id = msProduct.id');
$q->select('Vendor.id, Vendor.name, Vendor.logo, Vendor.description');
$q->groupby('Vendor.id');
$q->sortby('Vendor.name');

$q->limit($limit);

$parents = $modx->getChildIds($category, $depth);
$parents[] = $category;

$q->where(array('msProduct.parent:IN' => $parents));
$q->orCondition(array('Member.category_id:IN' => $parents));

if ($q->prepare() && $q->stmt->execute()) {
    $res = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
    foreach ($res as $elem) {
        echo '<div>'.$elem['id'].' - '.$elem['name'].' - '.$elem['logo'].' - '.$elem['description'].'</div>';
    }
}
вызываю его так [[!fabriki?]] на отдельной странице странице "Бренды" я понимаю, что нужно из msProduct брать ссылку наверное, но как не понимаю.. но могу и ошибаться
Саша Иващенко
?
Сергей Щерба
Я хочу вывести на страницу все товары одного производителя (для меню производителей). Как мне это сделать?
Сергей Щерба
Все, сделал. Это оказалось легко - просто в getPage прописал &where={"vendor.id":1} и все :)
Сергей Щерба
На локалке было все отлично, а вот на хосте выдает такие ошибки, ругаясь на where: (ERROR @ /index.php) [pdoTools] Error 42S22: Unknown column 'vendor.id' in 'where clause'
Вызов:
[[!getPage?
    &element=`msProducts`
    &includeContent=`1`
    &tpl=`tpl.msProducts.row`
    &parents=`29,24,44,45,46,47`
    &limit=`12`
    &pageNavOuterTpl=`[[+prev]][[+pages]][[+next]]`
    &pagePrevTpl=`<li class="control"><a[[+classes]][[+title]] href="[[+href]]"><</a></li>`
    &pageNextTpl=`<li class="control"><a[[+classes]][[+title]] href="[[+href]]">></a></li>`
    &where=`{"vendor.id:=":3}`
]]
Если getResources, то ошибка такая:
(ERROR @ /index.php) Error 42S22 executing statement: 
Array
(
    [0] => 42S22
    [1] => 1054
    [2] => Unknown column 'vendor.id' in 'where clause'
)
Подскажите из-за чего могут быть проблемы?
Василий Наумкин
Vendor должен быть с большой буквы.
Сергей Щерба
Спасибо, все заработало.
Андрей Завьялов
Ребята, что я делаю не так? Речь идет о выводе всех категорий и мультикатегорий, к которым принадлежит товар.
Создал сниппет, скопировал туда этот код.
Вызываю его [[$название_сниппета]] в чанке, чанк в шаблоне.
Пусто…
Андрей Завьялов
Всё, протупил. Вместо $ нужно !
НО Есть еще вопрос. Как выводить категории, глубиной в 0?
Т.е. сейчас мне сниппет выводит категорию и подкатегорию, в которой он лежит. А мне нужно, чтобы выводились только категории.
Спасибо!
Андрей Завьялов
Никто не подскажет?
Василий Наумкин
Сам подумай.
Наверное, нужно выбрать тех, у кого parent = 0?
Андрей Завьялов
Это я прекрасно понимаю, только я не знаю что именно нужно изменить в коде сниппета=) Подскажите пожалуйста строчку.
Андрей Завьялов
Хм... Если мы выберем только тех, у кого parent=0, мне отобразится только непосредственно сами категории.
У меня структура такая:
- Каталог (2)
    - Porsche (167)
        - Panamera (213)
            - Диски, колеса (345)
                - Диск R20 (1432)
Дак вот на странице товара (Диск R20 (1432)) мне нужно вывести список категорий, в которых он лежит, таких как "Panamera (213)". Конкретно только этого уровня. Категорий и мультикатегорий.
Понадобилось выводить все категории товара с возможностью показа родителей этих категорий. Может кому-нибудь тоже пригодится. Например если мы имеем вот такое дерево:
-Бренд
---Крутойбренд
---Отстойныйбренд
-----Товар
-Материал
---Береза
---Дуб
---Ель
Отделка
---Железо
---Алюминий
---Камень
---Пластик
Например наш товар относится к категориям: Крутойбренд, Береза, Ель, Алюминий, Пластик. Кроме того, что товар уже стандартными функциями MS2 мы увидим в нужных нам категориях, мы также можем убить второго зайца создав из этих категорий характеристики товара, которые отобразятся на его странице в виде:
Характеристики: Бренд - крутойбренд Материал - береза, ель Отделка - алюминий, пластик
Вот дописанный код сниппета который это реализует:
<?php
$startid = "23";

if (empty($rid)) {$rid = $modx->resource->id;}
if (empty($pid)) {$pid = $modx->resource->parent;}
if (empty($delimeter)) {$delimeter = ', ';}
$scheme = $modx->getOption('link_tag_scheme', null, 'full', true);

$q = $modx->newQuery('msCategory');
$q->leftJoin('msCategoryMember', 'msCategoryMember', array(
    '`msCategory`.`id` = `msCategoryMember`.`category_id`'
));
$q->sortby('pagetitle','ASC');
$q->groupby('id');
$q->select(array('id','pagetitle'));
$q->where('`msCategoryMember`.`product_id` = '.$rid.' OR `id` = '.$pid);

$result = array();
if ($q->prepare() && $q->stmt->execute()) {
    while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        $result[] = $row['id'];
    }
}

$gparid = $modx-> getChildIds($startid, 1);
foreach ($gparid as $parents) {
    $url = $modx->makeUrl($parents);
    $parid = $modx-> getChildIds($parents, 1);

    foreach ($parid as $dir) {
        if (in_array($dir, $result)) {
            $url = $modx->makeUrl($dir);
            $page = $modx->getObject('modResource', $dir);
            $pagetitle = $page->get('pagetitle');
            $link[] = '<a href="'.$url.'">'.$pagetitle.'</a>';
        }
    }
    if (!empty($link)) {
        $page = $modx->getObject('modResource', $parents);
        $gpagetitle = $page->get('pagetitle');
        $gurl = $modx->makeUrl($parents);
        $dirs = implode($delimeter, $link);
        echo '<a href="'.$gurl.'">'.$gpagetitle.'</a> - '.$dirs.'</ br>';
        unset($link);
    }
}
Вероятно код слишком медленный, т.к. имеется вызов цикла внутри цикла и если категорий будет очень много будет тормозить... Но пока смог только так придумать. Если есть идеи как это быстрее реализовать очень хотелось бы их увидеть!
Максим Франц
Подскажите как вывести дочерние категории на заданную глубину? Попробовал использовать приведенный тут сниппет "Дерево ресурсов" Но у меня
<?php
$parent = 1;
$depth = 1; // глубина выборки по дочерним категориям
$template = 1;
$exclude_parents = array(999);

$ids = $modx->getChildIds($parent,$depth);
// выборка ресурсов
$q = $modx->newQuery('modResource', array('parent:IN' => $ids, 'OR:id:IN' => $ids));
$q->andCondition(array('id:NOT IN' => $exclude_parents, 'template' => $template)); //, 'isfolder=1'
$q->select('id,pagetitle,parent');

$resources = array();
if ($q->prepare() && $q->stmt->execute()) {
    while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        if ($row['parent'] == $parent) {
            if (isset($resources[$row['id']])) {
                $resources[$row['id']] = array_merge($resources[$row['id']], $row);
            }
            else {
                $resources[$row['id']] = $row;
                $resources[$row['id']]['children'] = array();
            }
        }
        else {
            $resources[$row['parent']]['children'][$row['id']] = $row;
        }
    }
}

echo'<pre>';print_r($resources);
выбирает на 2 уровня, вместо 1го.
Цель сделать боковое меню с подкатегориями
Максим Франц
if ($q->prepare() && $q->stmt->execute()) {
    while ($row = $q->stmt->fetch(PDO::FETCH_ASSOC)) {
        if ($row['parent'] == $parent) {
            if (isset($resources[$row['id']])) {
                $resources[$row['id']] = array_merge($resources[$row['id']], $row);
            }
            else {
                $resources[$row['id']] = $row;
            //  $resources[$row['id']]['children'] = array();
            }
        }
        else {
        //  $resources[$row['parent']]['children'][$row['id']] = $row;
        }
    }
}
Другая крайность :) Выборка только 1го уровня.
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
inna
02.11.2024, 12:17:25
ой... по бесплатным урокам по vesp все ссылки битые. А так хотелось...
Ivan CR
24.10.2024, 15:20:54
С днем рождения!!! Класс, что в твоей жизни есть такие интересные достижения.
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так. А в контроллере, скорее всего, ловить данные методом post.
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен. Спасибо, исправил!
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. ...
Василий Наумкин
20.03.2024, 21:21:52
Volledig!
Андрей
14.03.2024, 13:47:10
Василий! Как всегда очень круто! Моё почтение!
russel gal
09.03.2024, 20:17:18
> А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал...
Александр Наумов
27.01.2024, 03:06:18
Василий, спасибо! Извини, тупанул.
Василий Наумкин
22.01.2024, 07:43:20
Давай-давай!