Вывод перечня заказанных товаров в таблицу с заказами minishop2 (панель менеджера)
Доброго всем времени суток!
Требуется вывести перечень товаров, которые содержаться в заказе.
На сколько я понимаю, за все это мероприятие отвечает вот этот вот товарищ: core/components/minishop2/processors/mgr/orders/getlist.class.php
и на сколько я понял, за запрос к базе отвечают следующие части:
И вот что-то я все ни как не соображу как сделать… На сколько я понимаю, объект или класс msOrderAddress не описан в данном процессоре… или поправьте меня?
Буду признателен за подсказки, в какую сторону копать!
UPD 25.05.25
С задачей справился.
понадобилось всего пару строчек кода.
Всем, кто откликнулся огромное спасибо! Особенно Артуру Шевченко, который мужественно терпел мои тупые вопросы =))
Требуется вывести перечень товаров, которые содержаться в заказе.
На сколько я понимаю, за все это мероприятие отвечает вот этот вот товарищ: core/components/minishop2/processors/mgr/orders/getlist.class.php
$c->leftJoin('modUser', 'User');
$c->leftJoin('modUserProfile', 'UserProfile');
$c->leftJoin('msOrderStatus', 'Status');
$c->leftJoin('msDelivery', 'Delivery');
$c->leftJoin('msPayment', 'Payment');
$c->leftJoin('msOrderAddress', 'Address');
и$c->select(
$this->modx->getSelectColumns('msOrder', 'msOrder', '', ['status', 'delivery', 'payment'], true) . ',
msOrder.status as status_id, msOrder.delivery as delivery_id, msOrder.payment as payment_id,
UserProfile.fullname as customer, User.username as customer_username,
Status.name as status, Status.color, Delivery.name as delivery, Payment.name as payment'
);
на мои попытки в первую часть кода добавить$c->leftJoin('msOrderProduct', 'Product');
и во вторую частьProduct.name,
минишоп выдает пустую таблицу…И вот что-то я все ни как не соображу как сделать… На сколько я понимаю, объект или класс msOrderAddress не описан в данном процессоре… или поправьте меня?
Буду признателен за подсказки, в какую сторону копать!
UPD 25.05.25
С задачей справился.
понадобилось всего пару строчек кода.
$c->select(array(
"product_name"=>"GROUP_CONCAT(Products.name, ' - ', Products.count, 'шт ')",
));
$c->groupby('Products.order_id');
Однако, на эти строчки из-за своей тотальной не осведомлённости в xPDO, да и в целом, не осведомлённости в программировании, я потратил около недели.Всем, кто откликнулся огромное спасибо! Особенно Артуру Шевченко, который мужественно терпел мои тупые вопросы =))
Комментарии: 29
msOrdermsOrderProduct — опечатка?
Если минишоп выдает пустую таблицу, то — возможно — в логах админки есть что-нибудь?
Если минишоп выдает пустую таблицу, то — возможно — в логах админки есть что-нибудь?
Действительно опечатка, спасибо, исправил
А в логах следующее:
(ERROR @ /home/c/cz42644/test-iflower/public_html/core/xpdo/xpdo.class.php: 644) Could not load class: ms2_order_products from mysql.ms2_order_products.
(ERROR @ /home/c/cz42644/test-iflower/public_html/core/xpdo/xpdo.class.php: 644) Could not load class: msorderProducts from mysql.msorderproducts.
[2025-04-13 15:04:21] (ERROR @ /home/c/cz42644/test-iflower/public_html/core/xpdo/xpdo.class.php: 1827) No foreign key definition for parentClass: msOrder using relation alias: Product
Каждая ошибка повторяется по нескольку раз, но это я так понимаю, идет несколько обращений к бд и каждое порождает одну и тужу ошибку.
(ERROR @ /home/c/cz42644/test-iflower/public_html/core/xpdo/xpdo.class.php: 644) Could not load class: ms2_order_products from mysql.ms2_order_products.
(ERROR @ /home/c/cz42644/test-iflower/public_html/core/xpdo/xpdo.class.php: 644) Could not load class: msorderProducts from mysql.msorderproducts.
[2025-04-13 15:04:21] (ERROR @ /home/c/cz42644/test-iflower/public_html/core/xpdo/xpdo.class.php: 1827) No foreign key definition for parentClass: msOrder using relation alias: Product
Каждая ошибка повторяется по нескольку раз, но это я так понимаю, идет несколько обращений к бд и каждое порождает одну и тужу ошибку.
Самое главное: нет конкретики…
Что за список товаров? Если в админке — он и так выводится в списке заказов, когда модалку открываешь. Если в публичной части сайта, к примеру, в ЛК, то зачем тащить процессор? Не проще ли составить нужный запрос в сниппете и в зависимости от id заказа цеплять товары? Ну, плюс ещё проверять принадлежность заказа к юзеру.
Что за список товаров? Если в админке — он и так выводится в списке заказов, когда модалку открываешь. Если в публичной части сайта, к примеру, в ЛК, то зачем тащить процессор? Не проще ли составить нужный запрос в сниппете и в зависимости от id заказа цеплять товары? Ну, плюс ещё проверять принадлежность заказа к юзеру.
Действительно! Самого главного я и не обозначил! Спасибо! Поправлю заголовок.
Речь идет именно о панели менеджера и возможности управления заказами.
В модалке без условно выводится и с этим нет сложностей, вопрос в том, что в данном контексте требуется перечень товаров выводить в общую таблицу.
Ну т.е. на равне с комментарием к заказу, датой заказа и прочими данными, требуется вывести перечень заказов.
Речь идет именно о панели менеджера и возможности управления заказами.
В модалке без условно выводится и с этим нет сложностей, вопрос в том, что в данном контексте требуется перечень товаров выводить в общую таблицу.
Ну т.е. на равне с комментарием к заказу, датой заказа и прочими данными, требуется вывести перечень заказов.
Требуется вывести перечень товаров, которые содержаться в заказе.В админке он и так выводится, а чтобы с фронта запустить процессор нужно дать права пользователю.
Второй момент ты хочешь присоединить к одной строке много строк, для этого нужно завернуть много строк в json. Примерно так
$q = $this->modx->newQuery('modResource');
$q->leftJoin('msCategoryMember', 'Member', 'Product.id=Member.product_id');
$q->leftJoin(
'modResource',
'Category',
'Member.category_id=Category.id AND Category.class_key="msCategory" AND Category.id != Product.parent AND Category.published = 1'
);
$q->select(':categoriesSelect AS categories');
$this->replacements[':categoriesSelect'] = <<<EOD
CONCAT(
'[', GROUP_CONCAT(
DISTINCT CONCAT(
'{"alias":"', Category.alias, '", "rid":"', Category.id, '", "menuindex":"', Category.menuindex, '", "template":"', Category.template, '"}'
)
SEPARATOR ','
), ']'
)
EOD;
Если MySQL повыше можно использовать спец функции для создания json
Большое спасибо за ответ, но к сожалению я мало чего понял из него… Я даже не до конца понимаю, что такое фронт… Это фронт-енд? Т.е. панель администрирования? Или отработка скриптов?
Как я это себе вижу=) А ты меня поправь.
Те данные, что присутствуют в таблице с заказами, берутся из таблицы _ms2_orders, у меня уже получилось расширить эту таблицу данными из таблицы _ms2_order_addresses, это выглядело сложно, но я справился (скажу даже, что я не менял исходники, а сделал красиво, именно расширив своими файлами), т.к. нужный запрос уже присутствовал в процессоре (фильтрация заказов частично делается и по этой таблице). Я так понимаю, что за это отвечает эта часть кода $c->leftJoin('msOrderAddress', 'Address'); Т.е. дальше, я просто немного изменил SQL-запрос добавив выборку нужных полей.
Сейчас, мне нужно в запрос подключить еще одну таблицу, а именно _ms2_order_products, причем еще нужно сделать сортировку по id заказа, т.к. товаров в одном заказе может быть много.
Не получится ли сделать подобным образом?
или нужно идти по пути, который ты предложил?
Что такое msOrderAddress в данном случае $c->leftJoin('msOrderAddress', 'Address');? А самое главное откуда оно берется, это же не таблица в БД, а объект, класс или что-то еще? Где можно найти рождение msOrderAddress этого? Выше написанная строчка кода делает алиас в запросе, и при дальнейшем обращении я уже могу обращаться к таблице _ms2_order_addresses как Address.
Как я это себе вижу=) А ты меня поправь.
Те данные, что присутствуют в таблице с заказами, берутся из таблицы _ms2_orders, у меня уже получилось расширить эту таблицу данными из таблицы _ms2_order_addresses, это выглядело сложно, но я справился (скажу даже, что я не менял исходники, а сделал красиво, именно расширив своими файлами), т.к. нужный запрос уже присутствовал в процессоре (фильтрация заказов частично делается и по этой таблице). Я так понимаю, что за это отвечает эта часть кода $c->leftJoin('msOrderAddress', 'Address'); Т.е. дальше, я просто немного изменил SQL-запрос добавив выборку нужных полей.
Сейчас, мне нужно в запрос подключить еще одну таблицу, а именно _ms2_order_products, причем еще нужно сделать сортировку по id заказа, т.к. товаров в одном заказе может быть много.
Не получится ли сделать подобным образом?
или нужно идти по пути, который ты предложил?
Что такое msOrderAddress в данном случае $c->leftJoin('msOrderAddress', 'Address');? А самое главное откуда оно берется, это же не таблица в БД, а объект, класс или что-то еще? Где можно найти рождение msOrderAddress этого? Выше написанная строчка кода делает алиас в запросе, и при дальнейшем обращении я уже могу обращаться к таблице _ms2_order_addresses как Address.
Я не видел весь твой код, но думаю что $c это объект класса xPDOQuery, который является конструктором SQL-запросов. А $c->leftJoin(), $c->select() это всё вызовы методов конструктора запросов. После все манипуляций получается обычный SQL, который и отправляется в БД.
Если упрощать, то когда ты делаешь leftJoin одна таблица ставится рядом с другой так, чтобы совпали поля, которые ты указываешь в условии ON (или передаешь третьим параметром в метод leftJoin). Таким образом получается новая таблица, но с бОльшим количеством столбцов, при этом количество строк будет таким же как и в первой таблице. Например, если у тебя в первой таблице одна запись, а во второй две, в итоговой таблице будет одна запись. Именно поэтому ты не можешь просто взять и присоединить все товары, тебе нужно сгруппировать их по id заказа, упаковать их в одну строку и только потом присоединять. вот так можно получить товары для заказа с id = 594
Если упрощать, то когда ты делаешь leftJoin одна таблица ставится рядом с другой так, чтобы совпали поля, которые ты указываешь в условии ON (или передаешь третьим параметром в метод leftJoin). Таким образом получается новая таблица, но с бОльшим количеством столбцов, при этом количество строк будет таким же как и в первой таблице. Например, если у тебя в первой таблице одна запись, а во второй две, в итоговой таблице будет одна запись. Именно поэтому ты не можешь просто взять и присоединить все товары, тебе нужно сгруппировать их по id заказа, упаковать их в одну строку и только потом присоединять. вот так можно получить товары для заказа с id = 594
<?php
$q = $this->modx->newQuery('msOrder');
$q->leftJoin('msOrderProduct', 'Product', 'msOrder.id=Product.order_id');
$select = <<<EOD
CONCAT(
'[', GROUP_CONCAT(
DISTINCT CONCAT(
'{"name":"', Product.name, '", "count":"', Product.count, '", "price":"', Product.price, '"}'
)
SEPARATOR ','
), ']'
)
EOD;
$q->select('msOrder.id as order_id, :products AS products');
$q->where(['msOrder.id' => 594]);
$q->groupby('msOrder.id');
$q->prepare();
$sql = $q->toSQL();
$sql = str_replace(':products', $select,$sql);
$stmt = $modx->prepare($sql);
if($stmt->execute()){
$result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
print_r($result);
}
Спасибо за ответ!
Так, то не мой код, то код разработчика minishop2. Это штатный процессор getlist.class.php.
И выдержки кода, я привел именно из него =)
Вот ссылка на полный код github.com/modx-pro/miniShop2/blob/master/core/components/minishop2/processors/mgr/orders/getlist.class.php
Что же касается изначального вопроса.
Ранее, в качестве практики, я уже формировал подобную таблицу, но не со стороны панели управления сайта, а на фронтенд (я правильно употребляю термин? Т.е. на самом сайте, в открытом доступе).
Делал эту таблицу на основе готового снипета, который нашел на просторах интернета.
Там код существенно проще, для моего восприятия.
Там есть sql-запрос, понятный мне, и есть цикл, который выводит данные.
По сути вот запрос, с которым, я достаточно долго сидел, прежде чем оно заработало. Однако это, как раз тот запрос, который решает, ту проблему, о который ты пишешь (когда товаров много).
А самое главное, у меня есть четкое убеждение того, что мне как-то нужно придерживаться стилистики кода, которым написан процессор. Или нет? Просто нужно же не просто в базу запрос сформировать, а еще нужно этот запрос потом обработать =)
Так, то не мой код, то код разработчика minishop2. Это штатный процессор getlist.class.php.
И выдержки кода, я привел именно из него =)
Вот ссылка на полный код github.com/modx-pro/miniShop2/blob/master/core/components/minishop2/processors/mgr/orders/getlist.class.php
Что же касается изначального вопроса.
Ранее, в качестве практики, я уже формировал подобную таблицу, но не со стороны панели управления сайта, а на фронтенд (я правильно употребляю термин? Т.е. на самом сайте, в открытом доступе).
Делал эту таблицу на основе готового снипета, который нашел на просторах интернета.
Там код существенно проще, для моего восприятия.
Там есть sql-запрос, понятный мне, и есть цикл, который выводит данные.
По сути вот запрос, с которым, я достаточно долго сидел, прежде чем оно заработало. Однако это, как раз тот запрос, который решает, ту проблему, о который ты пишешь (когда товаров много).
$sQuery="SELECT
adr.id, adr.country, adr.region, adr.phone, adr.comment, ord.status, adr.text_address, ord.order_comment, ord.delivery, ord.num, ord.weight, adr.receiver, ord.cost,
(select GROUP_CONCAT(name, ' - ', count, 'шт ') as names from modx3_ms2_order_products where order_id = adr.id) as names
FROM modx3_ms2_order_addresses as adr
LEFT JOIN
modx3_ms2_orders as ord ON adr.id=ord.id ORDER BY adr.id DESC
а дальше уже обрабатываю полученную таблицу и вывожу$result = $modx->query($sQuery);
$res = $result->fetchAll(PDO::FETCH_ASSOC);
foreach($res as $v){
echo "$v[id]";
}
По сути, ты пишешь тоже самое, но только именно под бекенд (верно термин использую?), т.е. для панели администратора?А самое главное, у меня есть четкое убеждение того, что мне как-то нужно придерживаться стилистики кода, которым написан процессор. Или нет? Просто нужно же не просто в базу запрос сформировать, а еще нужно этот запрос потом обработать =)
Если речь про админку, то мне вообще непонятно, что ты пытаешься сделать и зачем? В админке есть интерфейс работы с заказами и в этом интерфейсе можно посмотреть перечень товаров в конкретном заказе.
Очень любопытный вопрос! Кастомизирую, так, что бы было удобно работать с таблицей, и нужная информация, для обработки заказа выводилась в таблицу, а не нужная, как раз таки пускай будет в модальном окне.
Если ты не готов погружаться в в недра ExtJs будет сложно. К тому же вывести в списке заказов список товаров для каждого заказа можно только в виде строки с разделителем или в виде json. Я бы сделал свой интерфейс вместо стандартного если нужна сложная кастомная логика.
А я думал, я уже погружаюсь… медленно правда, но тем не менее.
На сколько я тебя понял, то как раз, в том запросе, который я писал самостоятельно, именно это реализовано с помощью разделителя, по сути, меня такой вариант устроит более чем.
Вопрос только в том, что я не могу понять код процессора.
Как я уже писал выше, мне нужно понять что такое msOrderAddress, и как можно создать аналогичный, по всей видимости запрос:
Если не трудно, помоги разобраться.
На сколько я тебя понял, то как раз, в том запросе, который я писал самостоятельно, именно это реализовано с помощью разделителя, по сути, меня такой вариант устроит более чем.
(select GROUP_CONCAT(name, ' - ', count, 'шт ') as names
from modx3_ms2_order_products
where order_id = adr.id) as names
Верно же?Вопрос только в том, что я не могу понять код процессора.
Как я уже писал выше, мне нужно понять что такое msOrderAddress, и как можно создать аналогичный, по всей видимости запрос:
$c->leftJoin('msOrderAddress', 'Address');
ведь по логике вещей leftJoin это как раз таки присоединение таблицы, но вот какой? у минишопа нет такой таблицы msOrderAddress…Если не трудно, помоги разобраться.
А я думал, я уже погружаюсьНе, ты не погружаешься в ExtJs, кажется ты его ещё не видел))) Это js фреймворк на котором работает админка modx, т.е. тебе мало получить данные из БД, тебе нужно прокинуть эти данные в js, который отрисовывает админку. Есть вероятность, что прокинуть данные можно плагином, а вот модифицировать процессор через плагин вряд ли возможно. Если же ты будешь править исходники, то автоматически останешься без обновлений компонента, даже случайная переустановка снесёт все изменения. В общем, я бы так не делал. К тому же, за 6 лет работы с Modx, я сам в ExtJs не погружался, слава богу Modx позволяет делать страницы в админке со своими скриптами, для простой логики можно сделать страницы с помощью migx.
=))) ну значит не погружаюсь.
Что же касается исходников, я уже писал выше, что все изменения, которые я вношу в код у меня реализованы посредством подгрузки моих кастомных файлов.
Т.е. я смог сделать так, что бы модикс выводя таблицу с заказами минишопа, подгружал процессор не getlist.class.php, а мой собственный custom_getlist.class.php, то же самое и с файлами JS скриптов, которые мне требуется исправить. Другими словами, обновление мне нипочем.
А теперь я погружаюсь в ExtJs? =)))
Тут явно не требуется плагин… вернее, не так, у меня уже создан плагин, который грузит нужный мне процессор вместо штатного. Теперь мне лишь нужно «допилить» его до нужного мне состояния.
Что же касается исходников, я уже писал выше, что все изменения, которые я вношу в код у меня реализованы посредством подгрузки моих кастомных файлов.
Т.е. я смог сделать так, что бы модикс выводя таблицу с заказами минишопа, подгружал процессор не getlist.class.php, а мой собственный custom_getlist.class.php, то же самое и с файлами JS скриптов, которые мне требуется исправить. Другими словами, обновление мне нипочем.
А теперь я погружаюсь в ExtJs? =)))
Тут явно не требуется плагин… вернее, не так, у меня уже создан плагин, который грузит нужный мне процессор вместо штатного. Теперь мне лишь нужно «допилить» его до нужного мне состояния.
Наверное такой поход имеет место быть, правда я бы с удовольствием узнал подробности, как это работает.
Что то я упустил из вида этот комментарий.
Вот тут я описываю как подгружал свой процессор.
Вот тут я описываю как подгружал свой процессор.
leftJoin это как раз таки присоединение таблицы, но вот какой? у минишопа нет такой таблицы msOrderAddress…Конечно нет, msOrderAddress это название класса. Работать напрямую с БД не очень удобно, поэтому Modx использует XPDO для формирования запросов в БД, а чтобы это было возможно нужно описать какие поля есть в таблице, какой тип данных в них надо записывать и т.д. Для описания используют классы модели, и каждому классу соответствует xml схема. Посмотреть xml схему можно в папке компонента core/components/minishop2/model/schema
о! А вот это уже ближе к теме! Т.е. там я смогу найти как создан класс msOrderAddress? я правильно тебя понял?
смогу найти как создан классНе очень понимаю, что ты хочешь найти, но могу с уверенностью заявить класс создан руками человека))) А в схеме можно посмотреть соответствие таблиц и классов, чтобы можно было писать запросы через XPDO.
я сам не очень понимаю, что мне нужно найти=)))
Но ведь где-то описан класс msOrderAddress, думаю, что если у меня будет этот класс, на основе него, я смогу создать свой, который мне нужен =))
Но ведь где-то описан класс msOrderAddress, думаю, что если у меня будет этот класс, на основе него, я смогу создать свой, который мне нужен =))
Эх! Сразу бы, ты меня послал бы на эти схемы=)))
Бегло посмотрел, кажись это именно то, что мне нужно было…
По крайней мере, уже нашел, как подключить нужную мне таблицу.
Собственно, я с самого начала был на верном пути… но…
я писал
Вопрос в том, что мне даже в голову не приходило, что алиас, а именно Products, тоже где-то описан… я думал, это уже на мое усмотрение, ан нет, в схеме описано =)))
Бегло посмотрел, кажись это именно то, что мне нужно было…
По крайней мере, уже нашел, как подключить нужную мне таблицу.
Собственно, я с самого начала был на верном пути… но…
я писал
$c->leftJoin('msOrderProduct', 'Product');
а нужно было $c->leftJoin('msOrderProduct', 'Products');
Т.е. одна буква и все=)))Вопрос в том, что мне даже в голову не приходило, что алиас, а именно Products, тоже где-то описан… я думал, это уже на мое усмотрение, ан нет, в схеме описано =)))
я думал, это уже на мое усмотрение, ан нетКак говориться: учи матчасть))) Ты можешь сам задать алиас, но в этом случае тебе так же самостоятельно нужно задать условия для соединения таблиц
$q = $modx->newQuery('msOrder');
$q->leftJoin('msOrderProduct', 'Product', 'msOrder.id=Product.order_id AND Product.product_id = 190');
$q->select('Product.options as options');
$q->where(['msOrder.id' => 604]);
$q->prepare();
echo $q->toSQL();
$q->stmt->execute();
$r= $q->stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($r);
$q->leftJoin('msOrderProduct', 'Product', 'msOrder.id=Product.order_id
AND Product.product_id = 190');
о как, т.е. ты для существующего класса msOrderProduct, делаешь алисас Product, и описываешь условия джойна?
Ага. Собственно в одном из первых ответов я точно так же сделал.
так, то да, только для мня то тут написана абракадабра =)
$q->leftJoin('msOrderProduct', 'Product', 'msOrder.id=Product.order_id AND Product.product_id = 190');
а как в этой конструкции сделать AS, как в запросе, к примеру, select name from table_name as name_product?а если быть точнее, то в штатном процессоре есть вот такой вот запрос
$c->select(
$this->modx->getSelectColumns('msOrder', 'msOrder', '', ['status', 'delivery', 'payment'], true) . ',
msOrder.status as status_id, msOrder.delivery as delivery_id, msOrder.payment as payment_id,
UserProfile.fullname as customer, User.username as customer_username,
Address.region as region, Address.text_address as text_address, Address.city as city, Address.phone as phone,
Status.name as status, Status.color, Delivery.name as delivery, Payment.name as payment'
);
и если я дописываю, к примеру Product.name as name_product, а в системных настройках минишопа задаю дополнительное поле для таблицы заказов name_product, то все работает.Но, естественно, выводится только 1 товар, а не все. И, увы, финт ушами, который я проворачивал со стороны фронденда, уже не выходит.
$sQuery="SELECT
adr.id, adr.country, adr.region, adr.phone, adr.comment, ord.status, adr.text_address, ord.order_comment, ord.delivery, ord.num, ord.weight,
adr.receiver, ord.cost,
*********
(select GROUP_CONCAT(name, ' - ', count, 'шт ') as names from modx3_ms2_order_products where order_id = adr.id) as names
FROM modx3_ms2_order_addresses as adr
*********
LEFT JOIN
modx3_ms2_orders as ord ON adr.id=ord.id ORDER BY adr.id DESC
";
я так понимаю, что select это какой-то метод в процессоре, и в итоге выходит ошибка…как мне это:
(select GROUP_CONCAT(name, ' - ', count, 'шт ') as names from modx3_ms2_order_products where order_id = adr.id) as names
FROM modx3_ms2_order_addresses as adr
засунуть в это:$c->select(
$this->modx->getSelectColumns('msOrder', 'msOrder', '', ['status', 'delivery', 'payment'], true) . ',
msOrder.status as status_id, msOrder.delivery as delivery_id, msOrder.payment as payment_id,
UserProfile.fullname as customer, User.username as customer_username,
Address.region as region, Address.text_address as text_address, Address.city as city, Address.phone as phone,
Status.name as status, Status.color, Delivery.name as delivery, Payment.name as payment'
);
Я же готовый пример скинул — пользуйся. Могу лишь добавить, что $c->select() и все другие методы можно вызывать сколько угодно раз.
Спасибо за помощь! Разобрался, если интересно, то в посте сделал UPD, написал, как решил вопрос.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.