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

Доброго всем времени суток!
Требуется вывести перечень товаров, которые содержаться в заказе.
На сколько я понимаю, за все это мероприятие отвечает вот этот вот товарищ: 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, да и в целом, не осведомлённости в программировании, я потратил около недели.

Всем, кто откликнулся огромное спасибо! Особенно Артуру Шевченко, который мужественно терпел мои тупые вопросы =))
Дмитрий
13 апреля 2025, 20:28
modx.pro
1 496
0

Комментарии: 29

Alexey
14 апреля 2025, 08:05
+1
msOrdermsOrderProduct — опечатка?

Если минишоп выдает пустую таблицу, то — возможно — в логах админки есть что-нибудь?
    Дмитрий
    16 апреля 2025, 12:35
    0
    Действительно опечатка, спасибо, исправил
      Дмитрий
      16 апреля 2025, 13:16
      0
      А в логах следующее:
      (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

      Каждая ошибка повторяется по нескольку раз, но это я так понимаю, идет несколько обращений к бд и каждое порождает одну и тужу ошибку.
        Alexey
        16 апреля 2025, 22:04
        0
        Самое главное: нет конкретики…

        Что за список товаров? Если в админке — он и так выводится в списке заказов, когда модалку открываешь. Если в публичной части сайта, к примеру, в ЛК, то зачем тащить процессор? Не проще ли составить нужный запрос в сниппете и в зависимости от id заказа цеплять товары? Ну, плюс ещё проверять принадлежность заказа к юзеру.
          Дмитрий
          17 апреля 2025, 21:35
          0
          Действительно! Самого главного я и не обозначил! Спасибо! Поправлю заголовок.
          Речь идет именно о панели менеджера и возможности управления заказами.
          В модалке без условно выводится и с этим нет сложностей, вопрос в том, что в данном контексте требуется перечень товаров выводить в общую таблицу.
          Ну т.е. на равне с комментарием к заказу, датой заказа и прочими данными, требуется вывести перечень заказов.
      Артур Шевченко
      14 апреля 2025, 10:14
      +1
      Требуется вывести перечень товаров, которые содержаться в заказе.
      В админке он и так выводится, а чтобы с фронта запустить процессор нужно дать права пользователю.

      Второй момент ты хочешь присоединить к одной строке много строк, для этого нужно завернуть много строк в 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
        Дмитрий
        16 апреля 2025, 13:06
        0
        Большое спасибо за ответ, но к сожалению я мало чего понял из него… Я даже не до конца понимаю, что такое фронт… Это фронт-енд? Т.е. панель администрирования? Или отработка скриптов?
        Как я это себе вижу=) А ты меня поправь.
        Те данные, что присутствуют в таблице с заказами, берутся из таблицы _ms2_orders, у меня уже получилось расширить эту таблицу данными из таблицы _ms2_order_addresses, это выглядело сложно, но я справился (скажу даже, что я не менял исходники, а сделал красиво, именно расширив своими файлами), т.к. нужный запрос уже присутствовал в процессоре (фильтрация заказов частично делается и по этой таблице). Я так понимаю, что за это отвечает эта часть кода $c->leftJoin('msOrderAddress', 'Address'); Т.е. дальше, я просто немного изменил SQL-запрос добавив выборку нужных полей.
        Сейчас, мне нужно в запрос подключить еще одну таблицу, а именно _ms2_order_products, причем еще нужно сделать сортировку по id заказа, т.к. товаров в одном заказе может быть много.
        Не получится ли сделать подобным образом?
        или нужно идти по пути, который ты предложил?

        Что такое msOrderAddress в данном случае $c->leftJoin('msOrderAddress', 'Address');? А самое главное откуда оно берется, это же не таблица в БД, а объект, класс или что-то еще? Где можно найти рождение msOrderAddress этого? Выше написанная строчка кода делает алиас в запросе, и при дальнейшем обращении я уже могу обращаться к таблице _ms2_order_addresses как Address.
          Артур Шевченко
          16 апреля 2025, 22:39
          +2
          Я не видел весь твой код, но думаю что $c это объект класса xPDOQuery, который является конструктором SQL-запросов. А $c->leftJoin(), $c->select() это всё вызовы методов конструктора запросов. После все манипуляций получается обычный SQL, который и отправляется в БД.
          Если упрощать, то когда ты делаешь 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);
          }
            Дмитрий
            19 апреля 2025, 11:51
            0
            Спасибо за ответ!
            Так, то не мой код, то код разработчика 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]";
            }
            По сути, ты пишешь тоже самое, но только именно под бекенд (верно термин использую?), т.е. для панели администратора?

            А самое главное, у меня есть четкое убеждение того, что мне как-то нужно придерживаться стилистики кода, которым написан процессор. Или нет? Просто нужно же не просто в базу запрос сформировать, а еще нужно этот запрос потом обработать =)
              Артур Шевченко
              19 апреля 2025, 11:56
              0
              Если речь про админку, то мне вообще непонятно, что ты пытаешься сделать и зачем? В админке есть интерфейс работы с заказами и в этом интерфейсе можно посмотреть перечень товаров в конкретном заказе.
                Дмитрий
                20 апреля 2025, 10:31
                0
                Очень любопытный вопрос! Кастомизирую, так, что бы было удобно работать с таблицей, и нужная информация, для обработки заказа выводилась в таблицу, а не нужная, как раз таки пускай будет в модальном окне.
                  Артур Шевченко
                  20 апреля 2025, 11:07
                  0
                  Если ты не готов погружаться в в недра ExtJs будет сложно. К тому же вывести в списке заказов список товаров для каждого заказа можно только в виде строки с разделителем или в виде json. Я бы сделал свой интерфейс вместо стандартного если нужна сложная кастомная логика.
                    Дмитрий
                    20 апреля 2025, 14:23
                    0
                    А я думал, я уже погружаюсь… медленно правда, но тем не менее.
                    На сколько я тебя понял, то как раз, в том запросе, который я писал самостоятельно, именно это реализовано с помощью разделителя, по сути, меня такой вариант устроит более чем.
                    (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…

                    Если не трудно, помоги разобраться.
                      Артур Шевченко
                      20 апреля 2025, 15:11
                      0
                      А я думал, я уже погружаюсь
                      Не, ты не погружаешься в ExtJs, кажется ты его ещё не видел))) Это js фреймворк на котором работает админка modx, т.е. тебе мало получить данные из БД, тебе нужно прокинуть эти данные в js, который отрисовывает админку. Есть вероятность, что прокинуть данные можно плагином, а вот модифицировать процессор через плагин вряд ли возможно. Если же ты будешь править исходники, то автоматически останешься без обновлений компонента, даже случайная переустановка снесёт все изменения. В общем, я бы так не делал. К тому же, за 6 лет работы с Modx, я сам в ExtJs не погружался, слава богу Modx позволяет делать страницы в админке со своими скриптами, для простой логики можно сделать страницы с помощью migx.
                        Дмитрий
                        20 апреля 2025, 15:23
                        0
                        =))) ну значит не погружаюсь.
                        Что же касается исходников, я уже писал выше, что все изменения, которые я вношу в код у меня реализованы посредством подгрузки моих кастомных файлов.
                        Т.е. я смог сделать так, что бы модикс выводя таблицу с заказами минишопа, подгружал процессор не getlist.class.php, а мой собственный custom_getlist.class.php, то же самое и с файлами JS скриптов, которые мне требуется исправить. Другими словами, обновление мне нипочем.
                        А теперь я погружаюсь в ExtJs? =)))

                        Тут явно не требуется плагин… вернее, не так, у меня уже создан плагин, который грузит нужный мне процессор вместо штатного. Теперь мне лишь нужно «допилить» его до нужного мне состояния.
                          Артур Шевченко
                          20 апреля 2025, 16:52
                          0
                          Наверное такой поход имеет место быть, правда я бы с удовольствием узнал подробности, как это работает.
                          Дмитрий
                          28 апреля 2025, 21:46
                          0
                          Что то я упустил из вида этот комментарий.
                          Вот тут я описываю как подгружал свой процессор.
                      Артур Шевченко
                      20 апреля 2025, 15:20
                      0
                      leftJoin это как раз таки присоединение таблицы, но вот какой? у минишопа нет такой таблицы msOrderAddress…
                      Конечно нет, msOrderAddress это название класса. Работать напрямую с БД не очень удобно, поэтому Modx использует XPDO для формирования запросов в БД, а чтобы это было возможно нужно описать какие поля есть в таблице, какой тип данных в них надо записывать и т.д. Для описания используют классы модели, и каждому классу соответствует xml схема. Посмотреть xml схему можно в папке компонента core/components/minishop2/model/schema
                        Дмитрий
                        20 апреля 2025, 15:24
                        0
                        о! А вот это уже ближе к теме! Т.е. там я смогу найти как создан класс msOrderAddress? я правильно тебя понял?
                          Артур Шевченко
                          20 апреля 2025, 16:54
                          0
                          смогу найти как создан класс
                          Не очень понимаю, что ты хочешь найти, но могу с уверенностью заявить класс создан руками человека))) А в схеме можно посмотреть соответствие таблиц и классов, чтобы можно было писать запросы через XPDO.
                          Дмитрий
                          20 апреля 2025, 20:08
                          0
                          я сам не очень понимаю, что мне нужно найти=)))
                          Но ведь где-то описан класс msOrderAddress, думаю, что если у меня будет этот класс, на основе него, я смогу создать свой, который мне нужен =))
                        Дмитрий
                        20 апреля 2025, 20:39
                        0
                        Эх! Сразу бы, ты меня послал бы на эти схемы=)))
                        Бегло посмотрел, кажись это именно то, что мне нужно было…
                        По крайней мере, уже нашел, как подключить нужную мне таблицу.
                        Собственно, я с самого начала был на верном пути… но…
                        я писал
                        $c->leftJoin('msOrderProduct', 'Product');
                        а нужно было
                        $c->leftJoin('msOrderProduct', 'Products');
                        Т.е. одна буква и все=)))
                        Вопрос в том, что мне даже в голову не приходило, что алиас, а именно Products, тоже где-то описан… я думал, это уже на мое усмотрение, ан нет, в схеме описано =)))
                          Артур Шевченко
                          20 апреля 2025, 20:50
                          0
                          я думал, это уже на мое усмотрение, ан нет
                          Как говориться: учи матчасть))) Ты можешь сам задать алиас, но в этом случае тебе так же самостоятельно нужно задать условия для соединения таблиц
                          $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);
                          Дмитрий
                          20 апреля 2025, 20:56
                          0
                          $q->leftJoin('msOrderProduct', 'Product', 'msOrder.id=Product.order_id 
                          AND Product.product_id = 190');
                          о как, т.е. ты для существующего класса msOrderProduct, делаешь алисас Product, и описываешь условия джойна?
                          Артур Шевченко
                          20 апреля 2025, 20:59
                          0
                          Ага. Собственно в одном из первых ответов я точно так же сделал.
                          Дмитрий
                          20 апреля 2025, 22:05
                          0
                          так, то да, только для мня то тут написана абракадабра =)
                          Дмитрий
                          21 апреля 2025, 10:52
                          0
                          $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'
                                  );
                          Артур Шевченко
                          21 апреля 2025, 14:20
                          0
                          Я же готовый пример скинул — пользуйся. Могу лишь добавить, что $c->select() и все другие методы можно вызывать сколько угодно раз.
                          Дмитрий
                          25 мая 2025, 22:03
                          0
                          Спасибо за помощь! Разобрался, если интересно, то в посте сделал UPD, написал, как решил вопрос.
      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
      29