Полезные выборки 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.

Заключение

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

← Предыдущая заметка
Очень хитрые TV параметры
Следующая заметка →
Дополнительное поле в свойствах товара в 2 клика
Комментарии (21)
alex.vakhitovAlex Vakhitov
07.05.2013 08:49

Люблю ORM modx все просто и понятно, в действительности выходит что просто пишешь текс с пожеланиями что хочешь получить (:

vbatushevВиталий Батушев
07.05.2013 09:12

Но почему без pdoTools?!

bezumkinВасилий Наумкин
07.05.2013 10:21

А зачем оно тут? Нет ни сложного построения запроса, ни дополнительных выборок, ни рендера чанков.

Это просто чистые выборки, которые можно интегрировать куда угодно, в том числе и в pdoTools-сниппет.

Андрей
07.05.2013 14:47

Не выводит цвета вторая выборка. У меня поле цвет лежит в таблице 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);
}
bezumkinВасилий Наумкин
07.05.2013 15:05

Естественно.

Ты же изменил поле, и теперь оно хранит не массив (который копируется в таблицу опций), а строку. То, что ты получаешь без указания категории - это старые записи, скорее всего, уже не актуальные.

Так что да, тебе нужна простейшая выборка одного поля из одной таблицы, как ты и сделал.

Саша Иващенко
14.06.2013 18:45

А как в "Вывод производителей товаров 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 брать ссылку наверное, но как не понимаю.. но могу и ошибаться

Саша Иващенко
18.06.2013 16:16

?

krontillСергей Щерба
20.06.2013 10:04

Я хочу вывести на страницу все товары одного производителя (для меню производителей). Как мне это сделать?

krontillСергей Щерба
20.06.2013 10:51

Все, сделал. Это оказалось легко - просто в getPage прописал &where={"vendor.id":1} и все :)

krontillСергей Щерба
20.06.2013 16:12

На локалке было все отлично, а вот на хосте выдает такие ошибки, ругаясь на 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'
)

Подскажите из-за чего могут быть проблемы?

bezumkinВасилий Наумкин
20.06.2013 16:41

Vendor должен быть с большой буквы.

krontillСергей Щерба
20.06.2013 16:54

Спасибо, все заработало.

Андрей Завьялов
16.10.2013 18:21

Ребята, что я делаю не так? Речь идет о выводе всех категорий и мультикатегорий, к которым принадлежит товар.

Создал сниппет, скопировал туда этот код.

Вызываю его [[$название_сниппета]] в чанке, чанк в шаблоне.

Пусто…

Андрей Завьялов
16.10.2013 18:44

Всё, протупил. Вместо $ нужно !

НО Есть еще вопрос. Как выводить категории, глубиной в 0?

Т.е. сейчас мне сниппет выводит категорию и подкатегорию, в которой он лежит. А мне нужно, чтобы выводились только категории.

Спасибо!

Андрей Завьялов
17.10.2013 12:45

Никто не подскажет?

bezumkinВасилий Наумкин
17.10.2013 12:55

Сам подумай.

Наверное, нужно выбрать тех, у кого parent = 0?

Андрей Завьялов
17.10.2013 13:29

Это я прекрасно понимаю, только я не знаю что именно нужно изменить в коде сниппета=) Подскажите пожалуйста строчку.

Андрей Завьялов
17.10.2013 14:07

Хм... Если мы выберем только тех, у кого parent=0, мне отобразится только непосредственно сами категории.

У меня структура такая:

- Каталог (2)
    - Porsche (167)
        - Panamera (213)
            - Диски, колеса (345)
                - Диск R20 (1432)

Дак вот на странице товара (Диск R20 (1432)) мне нужно вывести список категорий, в которых он лежит, таких как "Panamera (213)". Конкретно только этого уровня. Категорий и мультикатегорий.

unman64Даниил
09.12.2013 16:28

Понадобилось выводить все категории товара с возможностью показа родителей этих категорий. Может кому-нибудь тоже пригодится. Например если мы имеем вот такое дерево:

-Бренд
---Крутойбренд
---Отстойныйбренд
-----Товар
-Материал
---Береза
---Дуб
---Ель
Отделка
---Железо
---Алюминий
---Камень
---Пластик

Например наш товар относится к категориям: Крутойбренд, Береза, Ель, Алюминий, Пластик. Кроме того, что товар уже стандартными функциями 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);
    }
}

Вероятно код слишком медленный, т.к. имеется вызов цикла внутри цикла и если категорий будет очень много будет тормозить... Но пока смог только так придумать. Если есть идеи как это быстрее реализовать очень хотелось бы их увидеть!

Максим Франц
21.03.2014 11:56

Подскажите как вывести дочерние категории на заданную глубину? Попробовал использовать приведенный тут сниппет "Дерево ресурсов" Но у меня

<?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го.

Цель сделать боковое меню с подкатегориями

Максим Франц
21.03.2014 12:36
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
Василий Наумкин
09.04.2024 01:45
Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. Во...
futuris
Futuris
04.04.2024 05:56
Я просто немного запутался. Когда в абзаце &quot;Vesp/Core&quot; ты пишешь про &quot;новый trait Fil...
bezumkin
Василий Наумкин
20.03.2024 18:21
Volledig!
Андрей
14.03.2024 10:47
Василий! Как всегда очень круто! Моё почтение!
russelgal
russel gal
09.03.2024 17:17
А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал ...
inetlover
Александр Наумов
27.01.2024 00:06
Василий, спасибо! Извини, тупанул.
bezumkin
Василий Наумкин
22.01.2024 04:43
Давай-давай!
bezumkin
Василий Наумкин
24.12.2023 11:26
Спасибо!
bezumkin
Василий Наумкин
27.11.2023 02:43
Ура!
bezumkin
Василий Наумкин
25.11.2023 08:30
Vesp тянет 2 зависимости: vesp-frontent для фронта и vesp-core для бэкенда. Их можно обновлять, но э...