Подготавливаем ЛК для "ГдеПосылка"


Это продолжение статей по работе с заказами MS2, в последней статье я обещал, что расскажу как интегрировать сервис «ГдеПосылка» в Minishop2, а пока я жду пока очухается администрация «ГдеПосылка» чтобы дать мне доступ к API подготовим почву для интеграции, что мы будем делать:

  1. Добавим новое поле в заказы minishop2
  2. Сделаем вывод для пользователей их историю заказов без extJS и каких либо дополнений, на чистом pdoTools
  3. Научимся делать leftJoin и select на pdoTools и pdoFetch
  4. Бонусом идут сразу готовые стили для всего этого добра и написанный JS
За стилизацию заказов я брал пример с modstore, я надеюсь никто не обидится, потому что мне кажется что у нового дизайна модстора очень удачное стиливое решение истории заказов

За объяснением кода — под кат, за кодом на GitHub


Требования к фронту сайта:
  1. Jquery
  2. FontAwesome (не обязательно)
  3. FancyBox 3
Сразу опережая вопросы, вот как будет выглядеть подробная карточка заказа:


А теперь перейдем собственно к разработке:

Первое и самое важное — это добавляем новое поле к заказам MS2. Для этого воспользуемся разделом о плагинах товаров MS2 в официальной доке там написано как расширить свойства товара, но способ работает для любой модели MS2, это нам необходимо для того, чтобы MODX и MS2 увидел и мог работать с кастомными столбцами в БД.
Первым делом нам надо прописать новую карту объекта, для этого создадим index.php и msOrder.inc.php в index.php мы указываем карту какого объекта MS2 мы расширяем, а в msOrder.inc.php мы указываем какие поля добовляем, объяснять не вижу смысла, просто покажу файлы тут, если будут вопросы по ним — задавайте в комментариях

core/components/orderCustomField/index.php
<?php
return array(
    'map' => array(
        'msOrder' => require_once 'msOrder.inc.php',
    ),
);
core/components/orderCustomField/msOrder.inc.php
<?php 
return array(
    'fields' => array (
        'track' => NULL,
      ),
    'fieldMeta' => array(
        'track' => array (
          'dbtype' => 'varchar',
          'precision' => '100',
          'phptype' => 'string',
          'null' => true,
        ),
      ),
);
Также нам нужно добавить поле track в таблицу modx_ms2_orders, для этого в консоле выполним код:

$table = $modx->getTableName('msOrder');
$sql = 'ALTER TABLE ' . $table . '  ADD `track` VARCHAR(255) NULL;';
$modx->exec($sql);
Для того, чтобы minishop2 подхватил наш плагин — нам нужно выполнить код в консоле:
if ($miniShop2 = $modx->getService('miniShop2')) {
    $miniShop2->addPlugin('orderCustomField', '{core_path}components/orderCustomField/index.php');
}
На данном этапе MS2 уже полностью видит наше поле, вы можете с ним полноценно работать, добавлять его в письма к клиентам или обрабатывать на фронте, везде, где доступны данные заказов, но нам нужно само поле в админку, для этого создадим новый плагин на событие msOnManagerCustomCssJs с вот таким содержимым:
<?php
switch ($modx->event->name) {
    case 'msOnManagerCustomCssJs':
	//проверяем на какой странице мы находимся, если это страница orders, то добавляем наш html
        if ($page != 'orders') return;
	    $modx->controller->addHtml("
            <script type='text/javascript'>
                Ext.ComponentMgr.onAvailable('minishop2-window-order-update', function(){
                    this.fields.items[0].items[3].items[0].items.push(
                            {xtype: 'textfield', name: 'track', fieldLabel: 'Трек - номер', anchor: '100%'}
                        );     
                });           
            </script>");
    break;
}
Тут код не вижу смысла объяснять, он прост как три рубля, мы просто добавляем в массив extJS наш объект, скажу лишь только то, что если вы хотите добавить в какое-то другое место, то дебажте расположение элементов вот так:
console.log(this.fields)
А там уже по дереву объектов смотрите где находятся нужные вам поля и пуште в нужный массив ваш объект.

На этом добавление поля закончилось, перейдем к более интересному — выводу заказов пользователя!

Для вывода заказов мы будем использовать только pdoTools и pdoFetch, подразумевается, что у ваших пользователей уже есть ЛК и страница отведенная под историю заказов

На странице отведенную под историю заказов копируем содержимое файла:
/assets/components/MyCustomOrders/chunk/chunk.tpl.html
html и css я не вижу смысла рассматривать, а вот вызов pdoTools думаю будет многим интересен:

[[!pdoResources?
      &class=`msOrder`
      &sortby=`createdon`
      &leftJoin=`{
        "msDelivery":{"class":"msDelivery", "on":"msDelivery.id = msOrder.delivery"},
        "msPayment":{"class":"msPayment", "on":"msPayment.id = msOrder.payment"},
        "msOrderStatus":{"class":"msOrderStatus", "on":"msOrderStatus.id = msOrder.status"},
        "msOrderAddress":{"class":"msOrderAddress", "on":"msOrderAddress.id = msOrder.address"}
      }`
      &select=`{
        "msOrder": "*",
        "msDelivery": "msDelivery.name as deliveryName",
        "msPayment": "msPayment.name as paymentName",
        "msOrderStatus": "msOrderStatus.name as statusName, msOrderStatus.color",
        "msOrderAddress": "*"
      }`
      &where=`{"msOrder.user_id":[[+modx.user.id]]}`
      &showLog=`0`
      &limit=`9999`
      &tpl=`order_row`
    ]]
И так, что же мы делаем в этом вызове?
Первым делом мы обращаемся к модели msOrder и достаем из нее только те записи, которые соответствуют id текущего пользователя, за это отвечает параметр where
&where=`{"msOrder.user_id":[[+modx.user.id]]}`
Далее мы подключаем таблицы msDelivery, msPayment, msOrderStatus и msOrderAddress через leftJoin
msDelivery нам нужен для того, чтобы узнать название метода доставки, msPayment для названия метода оплаты, а msOrderStatus нам нужен для названия статуса и для выбранного цвета текущего статуса (все эти пункты в msOrder лежат как id и по id текущего заказа мы достаем массив статуса, доставки и оплаты). В msOrderAddress лежат все поля введенные пользователем на моменте оформления заказа.
Теперь перейдем к select. В селекте мы указываем то, какие поля нам нужны от каждой таблицы, пойдем по порядку:
msOrder — выбираем все поля
msDelivery — нужно только название, его и выбираем и даем ему псевдоним deliveryName
msPayment — аналогично с msDelivery
msOrderStatus — выбираем имя, даем псевдоним и выбираем цвет
msOrderAddress — выбираем все

Чанк order_row лежит тут

Вот в принципе и все, копируем разметку, не забываем подключить JS и стили, единственное что можно рассмотреть отдельно, это то, как в модалке мы получаем данные, для этого у нас в js есть ajax запрос на контроллер, который через pdoFetch запускает выборку с аналогичными параметрами, что и pdoTools ну и еще одну выборку по msProductData, чтобы подтянуть позиции заказа.

Как то слишком сумбурный туториал получился, потому что я не особо понимаю что нужно разжевывать, а что и так очевидно, если возникли вопросы — задавайте в комментариях, главная цель, с которой писалась эта статья, это НАУЧИТЬ НОВИЧИКОВ, по этому непонятные вам вещи можем разобрать в комментариях.

P.s. кто ждет статьи про интеграцию «где посылка» советую сразу оставить заявку на доступ к их API, потому что думают они очень долго и только пару дней назад дали доступ, хотя оставил я заявку 2 недели назад
Pavel Zarubin
19 октября 2017, 11:37
modx.pro
17
4 215
+8
Поблагодарить автора Отправить деньги

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

Виталий Дощенко
19 октября 2017, 16:24
+5
За стилизацию заказов я брал пример с modstore, я надеюсь никто не обидится, потому что мне кажется что у нового дизайна модстора очень удачное стиливое решение истории заказов
Без проблем. Приятно, что нравится!


Выкладывай уже свой первый доп к нам ;)
    Pavel Zarubin
    19 октября 2017, 23:04
    0
    Скоро, если по времени все получится :)
    Волков Николай
    19 октября 2017, 23:09
    0
    Требования к фронту сайта:
    Jquery 3+ (на более низких версиях не тестировалось)
    Третий бутстрап c jQuery выше 2.2.4 не работает… Мне кажется, что бутстрап — это одна из must have в плане поддержки для компонентов.
      Pavel Zarubin
      20 октября 2017, 00:18
      0
      На jquery ниже третей я не проверял просто, скорее всего будет работать.
      По поводу бутстрапа промолчу, я считаю хенжество тянуть такие вещи как бутстрп, в проект, когда есть flexbox
        Волков Николай
        20 октября 2017, 00:22
        0
        github.com/pavel-one/Modx-Custom-Orders-MS2/blob/master/assets/components/MyCustomOrders/js/customOrders.js
        Решил код глянуть и в первой же строчке проблема есть…
          Pavel Zarubin
          20 октября 2017, 00:24
          0
          В чем проблема?
          Волков Николай
          20 октября 2017, 00:27
          1
          +2
          jquery.com/upgrade-guide/3.0/#deprecated-document-ready-handlers-other-than-jquery-function

          В общем, начиная с 3 версии в jQuery иначе реализовано многое и я сталкивался со случаями, когда не работает:
          $(document).ready(function() {
          Если я не ошибаюсь, то все из-за того, что раньше ready был event, а сейчас это Promise. Лично я пишу так:
          $.when($.ready).then(function(){...});
            Pavel Zarubin
            20 октября 2017, 00:31
            0
            Да ладно? Даже на 1.12 jquery работает все нормально с реди
            Вот пример:
            codepen.io/pavel_one/pen/RLdbEG
            Проверяйте пожалуйста, прежде чем написать что-то
              Волков Николай
              20 октября 2017, 00:34
              +1
              Естественно, что на 1.12 пашет, т.к. ДО 3 был ready, а после уже нет.
              Deprecated: document-ready handlers other than jQuery(function)

              Due to historical compatibility issues there are a multitude of ways to set a document ready handler. All of the following are equivalent and call the function fn when the document is ready:

              $(fn);
              $().ready(fn);
              $(document).ready(fn);
              $(«selector»).ready(fn);

              As of jQuery 3.0 the recommended way to add a ready handler is the first method, $(fn). As noted in the Event section, the $(document).on(«ready», fn) event form has slightly different semantics and was removed in jQuery 3.0.
                Pavel Zarubin
                20 октября 2017, 00:40
                0
                Вот этого не знал, спасибо
                  Волков Николай
                  20 октября 2017, 00:46
                  0
                  Не за что. Очень распространенная ошибка на самом деле. Раньше у document был event на готовность, а теперь Promise. Но как бы оно ни было в данном случае ВООБЩЕ по фигу, т.к. у тебя просто обработку на клик вешает, а для этого вообще дожидаться готовности не нужно да нет никаких причин ждать чего либо вообще, кроме загрузки непосредственного самого jQuery :-)
                  Короче, тупо убери document и просто click оставь и будет счастье
          Stan Ezersky
          21 октября 2017, 21:43
          0
          Все последние работы делаю на Bootstrap 3 + jQuery v3.2.1, если не требуется mSearch2
          Я, правда, многое вырезаю, но основной функционал оставляю.

          Вот пример, дабы не быть голословным.

          mSearch2 с jQuery v3.2.1 — это да, некорректная работа
          Волков Николай
          19 октября 2017, 23:13
          0
          "msOrderAddress":{"class":"msOrderAddress", "on":"msOrderAddress.id = msOrder.id"}
          Ошибка. У них связь:

          "msOrderAddress":{"class":"msOrderAddress", "on":"msOrderAddress.address= msOrder.id"}
            Pavel Zarubin
            20 октября 2017, 00:22
            0
            Да ладно? joxi.ru/zANd1x3IBWMJjm
            Код скопирован с продакшена и прямо сейчас работает на одном сайте, можете сделать в консоле выборку через pdoFetch и убедиться что вы неправы
              Волков Николай
              20 октября 2017, 00:30
              0
              github.com/bezumkin/miniShop2/blob/e8d674f0ebf80450c3bb3830dfd49413f44a84ff/core/components/minishop2/model/schema/minishop2.mysql.schema.xml#L278

              Вот, пожалуйста…

              Код скопирован с продакшена и прямо сейчас работает на одном сайте
              Ты фортовый, что тут еще скажешь :-) Видимо ошибка не всплыла, т.к. во всех заказах по одному товару и поэтому ID в таблицах шли вровень :-)
                Pavel Zarubin
                20 октября 2017, 00:37
                0
                Еще раз прошу проверять что-то, перед тем как написать, joxi.ru/D2PK71nTp9LjOm
                  Волков Николай
                  20 октября 2017, 00:40
                  0
                  Еще раз прошу не верить в то, что имеет место быть банальная опечатка:
                    Pavel Zarubin
                    20 октября 2017, 00:57
                    0
                    Вот тут вообще не понял, о чем вы? :)
                      Волков Николай
                      20 октября 2017, 01:01
                      0
                      'on' => 'msOrderAddress.id = msOrder.id'

                      На гите косяк у связи
                        Pavel Zarubin
                        20 октября 2017, 01:03
                        0
                        В какой строке я то понял, а как должно быть по вашему, вот этого я понять не могу
                      Pavel Zarubin
                      20 октября 2017, 01:05
                      0
                      А Еще
                      github.com/pavel-one/Modx-Custom-Orders-MS2/blob/7f37b69d4bcf1515a5dde3e191dd812e8c6aeedb/assets/components/MyCustomOrders/action/getOrder.php#L43
                      и
                      github.com/pavel-one/Modx-Custom-Orders-MS2/blob/7f37b69d4bcf1515a5dde3e191dd812e8c6aeedb/assets/components/MyCustomOrders/action/getOrder.php#L9

                      А тут что не так?
                        Pavel Zarubin
                        20 октября 2017, 01:07
                        +1
                        Не продуман момент для случаев, когда контекст не web
                        Уж такие вещи для обучающей статьи продумывать не имеет смысла, статья находится в разделе «Пошаговые инструкции», а не в разделе «Готовые решения»
                          Pavel Zarubin
                          20 октября 2017, 01:32
                          0
                          А Еще
                          github.com/pavel-one/Modx-Custom-Orders-MS2/blob/7f37b69d4bcf1515a5dde3e191dd812e8c6aeedb/assets/components/MyCustomOrders/action/getOrder.php#L43
                          и
                          github.com/pavel-one/Modx-Custom-Orders-MS2/blob/7f37b69d4bcf1515a5dde3e191dd812e8c6aeedb/assets/components/MyCustomOrders/action/getOrder.php#L9
                          Все таки еще раз что тут не так? Пока ты не ушел)
                            Волков Николай
                            20 октября 2017, 01:38
                            0
                            Ок. Через минут скину сюда код
                              Волков Николай
                              20 октября 2017, 01:59
                              0
                              Нашел еще ошибку:

                              в getOrder.php ты вначале:

                              define('MODX_API_MODE', true);

                              Далее во-первых нет проверки на пустой id в POST
                              Ну а во-вторых ты sendRedirect делаешь, если не AJAX.
                              Что интересно: MODX_API_MODE не даст запустить handleRequest… Не понятен redirect. Нужно сразу exit хотя бы или т.п.
                                Pavel Zarubin
                                20 октября 2017, 02:03
                                0
                                Редирект отлично срабатывает
                                ТЫК
                                  Волков Николай
                                  20 октября 2017, 02:24
                                  0
                                  Короче, вместо него $modx->sendErrorPage();
                                    Pavel Zarubin
                                    20 октября 2017, 02:27
                                    0
                                    Я знаю метод на редирект страницы ошибки, и если бы мне нужно было бы редиректить на страницу ошибки, я бы его использовал %)
                                Pavel Zarubin
                                20 октября 2017, 02:09
                                0
                                Далее во-первых нет проверки на пустой id в POST
                                А зачем она нужна? Если пользователь не подхватил ID то и запрос у него не пойдет, а если кто-то решит шаловливыми ручками поэксперементировать, то ему во первых не составит труда подставить туда любое значение чтобы обойти проверку, а во вторых ничего страшного не случится если id не придет
                                  Волков Николай
                                  20 октября 2017, 02:29
                                  0
                                  ну давай я передам в console не число а массив… Или что там в pdoFetch передается, чтобы преобразовать в более сложный where?
                                    Pavel Zarubin
                                    20 октября 2017, 02:34
                                    +1
                                    И ничего не произойдет, представляешь?) Однако если бы даже был бы шанс чему то произойти, то проверка на пустоту не оберегла бы от атаки, проверка на число — уберегла бы, но в нашем случае не произойдет ровным счетом ничего даже если массив придет. К слову нельзя передать массив через POST, он просто придет строкой
                Волков Николай
                19 октября 2017, 23:16
                -2
                Теперь нам нужно добавить собственно наше поле «track» в нашу БД

                Через phpMyAdmin перейдем в таблицу modx_ms2_orders -> структура и добавим поле в самый конец:
                Имя: track
                Тип: varchar
                Длина: 255
                Null: true
                Если будет писаться компонент в modstore, то вместо этого надо:

                $manager = $modx->getManager();
                и уже им таблицу изменять
                  Pavel Zarubin
                  20 октября 2017, 00:23
                  +4
                  Этот компонент писаться в modstore не будет, иначе бы и статей небыло бы
                  Артем
                  26 февраля 2018, 08:25
                  0
                  Все сделал по инструкции, в итоге на странице заказов белый лист и в логе
                  [2018-02-26 08:23:39] (ERROR @ /home/s1111/www/core/components/pdotools/model/pdotools/pdotools.class.php : 977) Unexpected token ':' in c307b02e5743bce69f94649b8a8d7c6d line 78, near '{"class":' <- there
                  Куда рыть?
                    Артем
                    26 февраля 2018, 08:46
                    0
                    переписал так и все заработало:
                    [[!pdoResources?
                          &class=`msOrder`
                          &sortby=`createdon`
                          &leftJoin=`{
                            "msDelivery":{
                            "class":"msDelivery", "on":"msDelivery.id = msOrder.delivery"
                            },
                            "msPayment":{
                            "class":"msPayment", "on":"msPayment.id = msOrder.payment"
                            },
                            "msOrderStatus":{
                            "class":"msOrderStatus", "on":"msOrderStatus.id = msOrder.status"
                            },
                            "msOrderAddress":{
                            "class":"msOrderAddress", "on":"msOrderAddress.id = msOrder.address"
                            }
                          }`
                          &select=`{
                            "msOrder": "*",
                            "msDelivery": "msDelivery.name as deliveryName",
                            "msPayment": "msPayment.name as paymentName",
                            "msOrderStatus": "msOrderStatus.name as statusName, msOrderStatus.color",
                            "msOrderAddress": "*"
                          }`
                          &where=`{
                          "msOrder.user_id":[[+modx.user.id]]
                          }`
                          &showLog=`0`
                          &limit=`9999`
                          &tpl=`order_row`
                        ]]
                    Vadim
                    11 марта 2020, 21:20
                    0
                    Заметил, что если в заказе будет больше 10 товаров, то выводятся только 10, остальные не выводятся.
                    Фиксанул вот таким образом:

                    joxi.ru/n2Yb80aCZOzkaA
                      Pavel Zarubin
                      12 марта 2020, 09:46
                      0
                      Для этого в pdoResources существует такой параметр как limit, также можно выводить через pdoPage для пагинации
                        Vadim
                        13 марта 2020, 15:22
                        0
                        Спасибо! А как можно получить изображения товаров при открытии popup в заказе? В response их нет, как их можно достать?
                          Pavel Zarubin
                          13 марта 2020, 15:33
                          0
                          Вам нужно приджойнить таблицу msProductFile по аналогии с другими
                            Vadim
                            13 марта 2020, 18:06
                            0
                            А можете подсказать, как это правильно сделать? Пробую вот так в файле getOrder.php
                            // get images
                                'msProductFile' => array(
                                  'class' => 'msProductFile',
                                    'on' => array(
                                    'msProduct.id = msProductFile.product_id',
                                    'msProductFile.parent' => 0,
                                    'msProductFile.type' => 'image',
                                   )
                                  )
                                ),
                                'select' => array(
                                  'msOrder' => '*',
                                  'msDelivery' => 'msDelivery.name as deliveryName',
                                  'msOrderStatus' => 'msOrderStatus.name as statusName',
                                  'msPayment' => 'msPayment.name as paymentName',
                                  'msOrderAddress' => '*',
                                  //
                                  'msProductFile' => 'all'
                                  ),
                      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                      45