Дублирование результатов запроса после leftJoin
Уважаемое сообщество, здравствуйте!
В ходе работы над сниппетом, возвращающим список всех заказов пользователя, возникла проблема:
в результирующем массиве информация о заказе продублирована столько раз, сколько товаров включено в заказ.
Пожалуйста, подскажите, каким образом можно вернуть результат присоединения таблицы товаров к заказу в рамках одного элемента массива?
Ниже код моегокостыля сниппета (код, относящийся к оформлению результатов выборки вырезан, т.к. к вопросу не относится):
Спасибо!
В ходе работы над сниппетом, возвращающим список всех заказов пользователя, возникла проблема:
в результирующем массиве информация о заказе продублирована столько раз, сколько товаров включено в заказ.
Пожалуйста, подскажите, каким образом можно вернуть результат присоединения таблицы товаров к заказу в рамках одного элемента массива?
Ниже код моего
<?php
//
if(!isset($user) || ($user === 0)){
return '<p>Ошибка! Идентификатор пользователя не определён.</p>';
}
//Задаём значения чанков оформления по-умолчанию
if(!isset($tpl)){ $tpl = 'tpl.profile.orders_list.row'; }
if(!isset($tplWrapper) || ($tplWrapper === '')){ $tplWrapper = 'tpl.profile.orders_list.wrapper'; }
if(!isset($idx)){ $idx = 1; }
$q = $modx->newQuery('msOrder');
$q->select(
array
(
'msOrder.id',
'msOrder.num',
'msOrder.createdon',
'msOrder.cost',
'msOrder.status',
'op.product_id',
'op.name'
)
);
$q->where(array('msOrder.user_id' => $user));
$q->sortby('msOrder.id','DESC');
$q->leftJoin('msOrderProduct','op', 'msOrder.id = op.order_id');
$query = $q->prepare();
$query->execute();
$orders = $query->fetchAll(PDO::FETCH_ASSOC);
$res = print_r($orders,1);
echo '<div class="ord-lst-wrap"><pre>';
echo $res;
echo '</pre></div>';
Ниже приведен результат работы сниппета, первые два пункта демонстрируют проблему (в заказе два товара):Array
(
[0] => Array
(
[id] => 9
[num] => 1511/6
[createdon] => 2015-11-17 09:43:21
[cost] => 17849.00
[status] => 1
[product_id] => 5553
[name] => Primavera (гостиная) Комод 1 /Глазово
)
[1] => Array
(
[id] => 9
[num] => 1511/6
[createdon] => 2015-11-17 09:43:21
[cost] => 17849.00
[status] => 1
[product_id] => 5205
[name] => Berlin 1 (гостиная) Шкаф МНЦ КОРПУС (01, Ясень шимо свентлый) /Глазово
)
[2] => Array
(
[id] => 8
[num] => 1511/5
[createdon] => 2015-11-15 09:00:35
[cost] => 7599.00
[status] => 1
[product_id] => 2034
[name] => (закрыт) Стенка Франк МА-190 Дуб кремона/Дуб феррара /Столлайн
)
[3] => Array
(
[id] => 7
[num] => 1511/4
[createdon] => 2015-11-10 03:09:59
[cost] => 26323.00
[status] => 1
[product_id] => 5788
[name] => Акцент орхидея Шкаф-купе 2300х1800х600/ясень шимо темный/шампань/Бумеранг
)
P.S.: самое простое решение — вовсе избежать leftJoin'а таблицы товаров заказа к запросу, а в оформленном выводе возвращать ссылку с id-заказа, которая вела бы на страничку со сниппетом, принимающим на вход id заказа и возвращающего оформленный список товаров этого заказа. Но, может быть, это возможно сделать в одном сниппете (без обработки результирующего массива на php — объединения элементов с одинаковым id и формированием массива товаров)? Куда копать?Спасибо!
Комментарии: 3
Дублирование результатов запроса после leftJoinЧтобы такого не было, нужно сгруппировать запрос:
$q->groupby('msOrder.id');
Если же нужно выбрать заказ + все его товары, то лучше не использовать leftJoin и добирать эти товары отдельным запросом во время оформления вывода заказа.
Ну а если нужно будет еще и сортировать по товарам, то тогда всё же оставляешь leftJoin и убираешь дубликаты уже во время оформления.
Большое спасибо за советы, Василий!
Так и поступаю: обрабатываю результаты выборки перед оформлением, сливая дубликаты в один элемент с массивом товаров.
Так и поступаю: обрабатываю результаты выборки перед оформлением, сливая дубликаты в один элемент с массивом товаров.
Вдруг моя поделка кому-нибудь будет полезна.
Ниже приведен код сниппета и его чанков оформления.
Сниппет возвращает список заказов конкретного пользователя вместе с товарами заказа и оформляет вывод в чанки.
На вход сниппет принимает параметры:
'user' => Пользователь, чьи заказы выводим;
'tpl' => Чанк оформления элемента списка заказов;
'tplWrapper' => Чанк оформления оболочки списка заказов;
'tplProducts' => Чанк оформления элемента списка продуктов в заказе;
'tplEmpty' => Чанк оформления пустого результата;
Код сниппета «getUserOrders»:
Ниже приведен код сниппета и его чанков оформления.
Сниппет возвращает список заказов конкретного пользователя вместе с товарами заказа и оформляет вывод в чанки.
На вход сниппет принимает параметры:
'user' => Пользователь, чьи заказы выводим;
'tpl' => Чанк оформления элемента списка заказов;
'tplWrapper' => Чанк оформления оболочки списка заказов;
'tplProducts' => Чанк оформления элемента списка продуктов в заказе;
'tplEmpty' => Чанк оформления пустого результата;
Код сниппета «getUserOrders»:
<?php
if(!isset($user) || ($user === 0))
{
return '<p>Ошибка! Идентификатор пользователя не определён.</p>';
}
//Задаём значения чанков оформления по-умолчанию
if(!isset($tpl)){ $tpl = 'tpl.profile.orders_list.row'; }
if(!isset($tplWrapper) || ($tplWrapper === '')){ $tplWrapper = 'tpl.profile.orders_list.wrapper'; }
if(!isset($tplProducts) || ($tplProducts === '')){ $tplProducts = 'tpl.profile.orders_list.products.row'; }
if(!isset($tplEmpty) || ($tplEmpty === '')){ $tplEmpty = 'tpl.profile.orders_list.empty'; }
if(!isset($idx)){ $idx = 1; }
//Формируем запрос к БД
$q = $modx->newQuery('msOrder');
$q->select(
array
(
'msOrder.id',
'msOrder.num',
'msOrder.createdon',
'msOrder.cost',
'msOrder.status',
'op.product_id',
'op.name',
'op.price',
'op.count'
)
);
$q->where(array('msOrder.user_id' => $user));
$q->sortby('msOrder.id','DESC');
$q->leftJoin('msOrderProduct','op', 'msOrder.id = op.order_id');
$query = $q->prepare();
$query->execute();
$orders = $query->fetchAll(PDO::FETCH_ASSOC);
if (count($orders) === 0)
{
$result = $modx->getChunk('tpl.profile.orders_list.empty');
return $result;
}
//Инициализируем переменные для обработки результата выборки
$orders_unique = array();
$count = 0;
foreach ($orders as $o)
{
//Т.к. в результате выборки дублируется информация по заказам с несколькими товарами -
//перед выводом избавляемся от дубликатов
//Если ID последнего элемента в обработанном списке заказов не совпадает с ID обрабатываемого заказа, либо это первый шаг,
//то формируем новый элемент обработанного массива, запихивая туда данные выборки
if (($o['id'] != $orders_unique[$count - 1]['order_id']) || ($count === 0))
{
$orders_unique[$count] = array
(
'order_id' => $o['id'],
'order_num' => $o['num'],
'order_createdon' => $o['createdon'],
'order_status' => $o['status'],
'order_total_cost' => $o['cost']
);
//Т.к. товаров в заказе может быть несколько - инициализируем поле order_products, как массив
$orders_unique[$count]['order_products'][] = array
(
'product_id' => $o['product_id'],
'product_name' => $o['name'],
'product_price' => $o['price'],
'product_count' => $o['count'],
'product_cost' => $o['price'] * $o['count']
);
$count++;
}
//В случае, если мы наткнулись на дубликат заказа, то просто дополняем массив 'order_products' последнего элемента обработанного списка
else
{
$orders_unique[$count - 1]['order_products'][] = array
(
'product_id' => $o['product_id'],
'product_name' => $o['name'],
'product_price' => $o['price'],
'product_count' => $o['count'],
'product_cost' => $o['price'] * $o['count']
);
}
}
//Вывод отладочной информации по формируемому массиву, если потребуется
/*$res = print_r($orders_unique, 1);
echo '<div class="ord-lst-wrap"><pre>';
echo $res;
echo '</pre></div>';*/
//Оформляем массив с отфильтрованными дубликатами
foreach ($orders_unique as $o_u)
{
$products_list = '';
//Обрабатываем каждый элемент массива товаров в заказе
foreach($o_u['order_products'] as $o_p)
{
$product_data = array
(
'product_id' => $o_p['product_id'],
'product_name' => $o_p['product_name'],
'product_price' => $o_p['product_price'],
'product_count' => $o_p['product_count'],
'product_cost' => $o_p['product_cost']
);
if ($tplProducts !== '')
{
$products_list .= $modx->getChunk($tplProducts, $product_data);
}
//В случае, если шаблон оформления товара не задан - выводим массив доступных плейсхолдеров вместе с содержимым
else
{
$products_list .= '<pre>'.print_r($product_data, 1).'</pre>';
}
}
$order_data = array
(
'order_num' => $o_u['order_num'],
'order_createdon' => $o_u['order_createdon'],
'order_total_cost' => $o_u['order_total_cost'],
'order_status' => $o_u['order_status'],
'order_products' => $products_list,
'idx' => $idx
);
if ($tpl !== '')
{
$output .= $modx->getChunk($tpl, $order_data);
}
//В случае, если шаблон оформления заказа не задан - выводим массив доступных плейсхолдеров вместе с содержимым
else
{
$output .= '<pre>'.print_r($order_data, 1).'</pre>';
}
$idx++;
}
$result = $modx->getChunk($tplWrapper,
array
(
'output' => $output,
'orders_total' => count($orders_unique)
)
);
return $result;
Содержимое чанка 'tpl.profile.orders_list.wrapper':<!-- tpl.profile.orders_list.wrapper-->
<div class="pure-g ord-lst-wrap">
<div class="pure-u-1">
<div class="pure-g ord-lst-body">
[[+output]]
</div>
</div>
<div class="pure-u-1">
<p>Всего заказов: [[+orders_total]]</p>
</div>
</div>
<!-- /tpl.profile.orders_list.wrapper-->
Содержимое чанка 'tpl.profile.orders_list.row':<!-- tpl.profile.orders_list.row -->
<div class="pure-g ord-lst-item" id="order-list-item-[[+idx]]">
<div class="pure-u-1">
<div class="pure-g">
<div class="pure-u-6-24"><span>Номер:[[+order_num]]</span></div>
<div class="pure-u-6-24"><span>Дата:[[+order_createdon]]</span></div>
<div class="pure-u-6-24"><span class="center">Стомость:[[+order_total_cost]] руб.</span></div>
<div class="pure-u-6-24"><span class="center">Статус:[[+order_status]]</span></div>
</div>
</div>
<div class="pure-u-1">
<div class="pure-g ord-prod-wrap">
<div class="pure-u-1">
<div class="pure-u-2-24"><span>ID товара</span></div>
<div class="pure-u-12-24"><span>Наименование</span></div>
<div class="pure-u-3-24"><span>Цена</span></div>
<div class="pure-u-3-24"><span>Количество</span></div>
<div class="pure-u-4-24"><span>Итого</span></div>
</div>
<div class="pure-u-1 ord-prod-lst">
[[+order_products]]
</div>
</div>
</div>
</div>
<!-- /tpl.profile.orders_list.row -->
Содержимое чанка 'tpl.profile.orders_list.products.row':<!-- tpl.profile.orders_list.products.row -->
<div class="pure-g order-products-item">
<div class="pure-u-2-24"><span>[[+product_id]]</span></div>
<div class="pure-u-12-24"><span>[[+product_name]]</span></div>
<div class="pure-u-3-24"><span>[[+product_price]] руб.</span></div>
<div class="pure-u-3-24"><span>[[+product_count]]</span></div>
<div class="pure-u-4-24"><span>[[+product_cost]] руб.</span></div>
</div>
<!-- /tpl.profile.orders_list.products.row -->
Содержимое чанка 'tpl.profile.orders_list.empty':<!-- tpl.profile.orders_list.empty -->
<div class="pure-g ord-lst-empty-wrap">
<div class="pure-u-1">
<p>Вы не сделали ни одного заказа.</p>
</div>
</div>
<!-- /tpl.profile.orders_list.empty -->
Вызов сниппета на странице:{$_modx->runSnippet('!getUserOrders',[
'user' => $_modx->user.id,
])}
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.