Passed
Pull Request — master (#1)
by Maxime
04:38
created

ReportRepository::getVariantsOptions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
c 0
b 0
f 0
dl 0
loc 23
rs 9.7666
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MonsieurBiz\SyliusSalesReportsPlugin\Repository;
6
7
use Doctrine\ORM\EntityRepository;
8
use Doctrine\ORM\QueryBuilder;
9
use MonsieurBiz\SyliusSalesReportsPlugin\Exception\InvalidDateException;
10
use MonsieurBiz\SyliusSalesReportsPlugin\Exception\MissingLocaleException;
11
use Sylius\Component\Core\Model\AdjustmentInterface;
12
use Sylius\Component\Core\Model\ChannelInterface;
13
use Sylius\Component\Core\Model\OrderInterface;
14
use Sylius\Component\Core\Model\PaymentInterface;
15
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
16
use Sylius\Component\Core\Repository\ProductVariantRepositoryInterface;
17
18
class ReportRepository
19
{
20
    // Adjustment if Admin Order Creation plugin is installed
21
    public const ADMIN_ORDER_DISCOUNT_ADJUSTMENT = 'order_discount';
22
    public const ADMIN_ORDER_ITEM_DISCOUNT_ADJUSTMENT = 'order_item_discount';
23
24
    /**
25
     * @var OrderRepositoryInterface
26
     */
27
    protected $orderRepository;
28
29
    /**
30
     * @var ProductVariantRepositoryInterface
31
     */
32
    protected $productVariantRepository;
33
34
    /**
35
     * Result with totals, one dimensional array
36
     *
37
     * @var array
38
     */
39
    protected $result;
40
41
    /**
42
     * Results with totals by elements, two dimensional array
43
     *
44
     * @var array
45
     */
46
    protected $results;
47
48
    /**
49
     * The elements we want to group, id as key, id or label as value, one dimensional array
50
     *
51
     * @var array
52
     */
53
    protected $elements;
54
55
    /**
56
     * An array of elements we want to group, two dimensional array
57
     *
58
     * @var array
59
     */
60
    protected $elementsArray;
61
62
    /**
63
     * ReportRepository constructor.
64
     * @param OrderRepositoryInterface $orderRepository
65
     * @param ProductVariantRepositoryInterface $productVariantRepository
66
     */
67
    public function __construct(OrderRepositoryInterface $orderRepository, ProductVariantRepositoryInterface $productVariantRepository)
68
    {
69
        $this->orderRepository = $orderRepository;
70
        $this->productVariantRepository = $productVariantRepository;
71
    }
72
73
    /**
74
     * Get total sales for channel between 2 date times, or average sales from a given field
75
     *
76
     * @param ChannelInterface $channel
77
     * @param \DateTimeInterface $from
78
     * @param \DateTimeInterface|null $to
79
     * @param string|null $groupField
80
     * @return array
81
     * @throws InvalidDateException
82
     */
83
    public function getSalesForChannelForDates(
84
        ChannelInterface $channel,
85
        \DateTimeInterface $from,
86
        ?\DateTimeInterface $to = null,
87
        ?string $groupField = null
88
    ): array {
89
        $to = $to ?? $from; // If to is null, take the same day as from to make report on one day
90
        try {
91
            $from = new \DateTime($from->format("Y-m-d")." 00:00:00");
92
            $to   = new \DateTime($to->format("Y-m-d")." 23:59:59");
93
        } catch (\Exception $e) {
94
            throw new InvalidDateException('Invalid date given to report.');
95
        }
96
97
        $this->initResult();
98
99
        // Order Item Units values
100
        $this->addResults($this->getOrderItemUnitValues($channel, $from, $to), $groupField);
101
        // Order Items values
102
        $this->addResults($this->getOrderItemValues($channel, $from, $to), $groupField);
103
        // Order values
104
        $this->addResults($this->getOrderValues($channel, $from, $to), $groupField);
105
106
        // Divide results by number of elements if needed
107
        $this->averageResult();
108
109
        return $this->result;
110
    }
111
112
113
    /**
114
     * Get average sales for channel between 2 date times
115
     *
116
     * @param ChannelInterface $channel
117
     * @param \DateTimeInterface $from
118
     * @param \DateTimeInterface|null $to
119
     * @return array
120
     * @throws InvalidDateException
121
     */
122
    public function getAverageSalesForChannelForDates(
123
        ChannelInterface $channel,
124
        \DateTimeInterface $from,
125
        ?\DateTimeInterface $to = null
126
    ): array {
127
        return $this->getSalesForChannelForDates($channel, $from, $to, 'order_id');
128
    }
129
130
    /**
131
     * Get sales per product variant for channel between 2 date times
132
     *
133
     * @param ChannelInterface $channel
134
     * @param \DateTimeInterface $from
135
     * @param \DateTimeInterface|null $to
136
     * @return array
137
     * @throws InvalidDateException
138
     */
139
    public function getProductVariantSalesForChannelForDates(
140
        ChannelInterface $channel,
141
        \DateTimeInterface $from,
142
        ?\DateTimeInterface $to = null
143
    ): array {
144
        $to = $to ?? $from; // If to is null, take the same day as from to make report on one day
145
        try {
146
            $from = new \DateTime($from->format("Y-m-d")." 00:00:00");
147
            $to   = new \DateTime($to->format("Y-m-d")." 23:59:59");
148
        } catch (\Exception $e) {
149
            throw new InvalidDateException('Invalid date given to report.');
150
        }
151
152
        $this->results = [];
153
154
        // Order Item Units values
155
        $this->addResultsByElement(
156
            $this->getOrderItemUnitValues($channel, $from, $to), 'variant_id', 'variant_name'
157
        );
158
        // Order Items values
159
        $this->addResultsByElement(
160
            $this->getOrderItemValues($channel, $from, $to), 'variant_id', 'variant_name'
161
        );
162
163
        return $this->results;
164
    }
165
166
    /**
167
     * Get sales per product option for channel between 2 date times
168
     *
169
     * @param ChannelInterface $channel
170
     * @param \DateTimeInterface $from
171
     * @param \DateTimeInterface|null $to
172
     * @return array
173
     * @throws InvalidDateException
174
     * @throws MissingLocaleException
175
     */
176
    public function getProductOptionSalesForChannelForDates(
177
        ChannelInterface $channel,
178
        \DateTimeInterface $from,
179
        ?\DateTimeInterface $to = null
180
    ): array {
181
        $to = $to ?? $from; // If to is null, take the same day as from to make report on one day
182
        try {
183
            $from = new \DateTime($from->format("Y-m-d")." 00:00:00");
184
            $to   = new \DateTime($to->format("Y-m-d")." 23:59:59");
185
        } catch (\Exception $e) {
186
            throw new InvalidDateException('Invalid date given to report.');
187
        }
188
189
        $this->results = [];
190
191
        // Order Item Units values
192
        $this->addResultsByElement(
193
            $this->getOrderItemUnitValues($channel, $from, $to), 'variant_id', 'variant_name'
194
        );
195
        // Order Items values
196
        $this->addResultsByElement(
197
            $this->getOrderItemValues($channel, $from, $to), 'variant_id', 'variant_name'
198
        );
199
200
        // Populate array with options values data
201
        if (!($locale = $channel->getDefaultLocale()) || (!$localeCode = $locale->getCode())) {
202
            throw new MissingLocaleException('Missing default locale for channel');
203
        }
204
        $resultsWithOptions = $this->populateOptions($localeCode);
205
206
        // Reinit results to generate a new one
207
        $this->results = [];
208
209
        $this->addResultsByElement($resultsWithOptions, 'option_code', 'option_label');
210
211
        return $this->results;
212
    }
213
214
    /**
215
     * Get sales per product option for channel between 2 date times
216
     *
217
     * @param ChannelInterface $channel
218
     * @param \DateTimeInterface $from
219
     * @param \DateTimeInterface|null $to
220
     * @return array
221
     * @throws InvalidDateException
222
     * @throws MissingLocaleException
223
     */
224
    public function getProductOptionValueSalesForChannelForDates(
225
        ChannelInterface $channel,
226
        \DateTimeInterface $from,
227
        ?\DateTimeInterface $to = null
228
    ): array {
229
        $to = $to ?? $from; // If to is null, take the same day as from to make report on one day
230
        try {
231
            $from = new \DateTime($from->format("Y-m-d")." 00:00:00");
232
            $to   = new \DateTime($to->format("Y-m-d")." 23:59:59");
233
        } catch (\Exception $e) {
234
            throw new InvalidDateException('Invalid date given to report.');
235
        }
236
237
        $this->results = [];
238
239
        // Order Item Units values
240
        $this->addResultsByElement(
241
            $this->getOrderItemUnitValues($channel, $from, $to), 'variant_id', 'variant_name'
242
        );
243
        // Order Items values
244
        $this->addResultsByElement(
245
            $this->getOrderItemValues($channel, $from, $to), 'variant_id', 'variant_name'
246
        );
247
248
        // Populate array with options values data
249
        if (!($locale = $channel->getDefaultLocale()) || (!$localeCode = $locale->getCode())) {
250
            throw new MissingLocaleException('Missing default locale for channel');
251
        }
252
        $resultsWithOptions = $this->populateOptions($localeCode);
253
254
        // Reinit results to generate a new one
255
        $this->results = [];
256
257
        $this->addResultsByElement($resultsWithOptions, 'option_value_code', 'option_value_label', ['option_code', 'option_label']);
258
259
        return $this->results;
260
    }
261
262
    /**
263
     * Get sales per product for channel between 2 date times
264
     *
265
     * @param ChannelInterface $channel
266
     * @param \DateTimeInterface $from
267
     * @param \DateTimeInterface|null $to
268
     * @return array
269
     * @throws InvalidDateException
270
     */
271
    public function getProductSalesForChannelForDates(
272
        ChannelInterface $channel,
273
        \DateTimeInterface $from,
274
        ?\DateTimeInterface $to = null
275
    ): array {
276
        $to = $to ?? $from; // If to is null, take the same day as from to make report on one day
277
        try {
278
            $from = new \DateTime($from->format("Y-m-d")." 00:00:00");
279
            $to   = new \DateTime($to->format("Y-m-d")." 23:59:59");
280
        } catch (\Exception $e) {
281
            throw new InvalidDateException('Invalid date given to report.');
282
        }
283
284
        $this->results = [];
285
286
        // Order Item Units values
287
        $this->addResultsByElement(
288
            $this->getOrderItemUnitValues($channel, $from, $to), 'product_id', 'product_name'
289
        );
290
        // Order Items values
291
        $this->addResultsByElement(
292
            $this->getOrderItemValues($channel, $from, $to), 'product_id', 'product_name'
293
        );
294
295
        return $this->results;
296
    }
297
298
    /**
299
     * Generate results for order item units
300
     *
301
     * @param ChannelInterface $channel
302
     * @param \DateTimeInterface $from
303
     * @param \DateTimeInterface $to
304
     * @return array
305
     */
306
    protected function getOrderItemUnitValues(
307
        ChannelInterface $channel,
308
        \DateTimeInterface $from,
309
        \DateTimeInterface $to
310
    ): array {
311
        $queryBuilder = $this->createOrderQuery()
312
            ->select($this->getSelectColumns(true, false, false))
313
            ->leftJoin('o.items', 'item')
314
            ->leftJoin('item.variant', 'variant')
315
            ->leftJoin('item.units', 'element')
316
        ;
317
        $queryBuilder = $this->appendAdjustmentsAndParameters($queryBuilder, $channel, $from, $to);
318
        return $queryBuilder->getQuery()->getArrayResult();
319
    }
320
321
    /**
322
     * Generate results for order items
323
     *
324
     * @param ChannelInterface $channel
325
     * @param \DateTimeInterface $from
326
     * @param \DateTimeInterface $to
327
     * @return array
328
     */
329
    protected function getOrderItemValues(
330
        ChannelInterface $channel,
331
        \DateTimeInterface $from,
332
        \DateTimeInterface $to
333
    ): array {
334
        $queryBuilder = $this->createOrderQuery()
335
            ->select($this->getSelectColumns(false, true, false))
336
            ->leftJoin('o.items', 'element')
337
            ->leftJoin('element.variant', 'variant')
338
        ;
339
        $queryBuilder = $this->appendAdjustmentsAndParameters($queryBuilder, $channel, $from, $to);
340
        return $queryBuilder->getQuery()->getArrayResult();
341
    }
342
343
    /**
344
     * Generate results for orders
345
     *
346
     * @param ChannelInterface $channel
347
     * @param \DateTimeInterface $from
348
     * @param \DateTimeInterface $to
349
     * @return array
350
     */
351
    protected function getOrderValues(
352
        ChannelInterface $channel,
353
        \DateTimeInterface $from,
354
        \DateTimeInterface $to
355
    ): array {
356
        $queryBuilder = $this->createOrderQuery()->select($this->getSelectColumns(false, false, true));
357
        $queryBuilder = $this->appendAdjustmentsAndParameters($queryBuilder, $channel, $from, $to, true);
358
        return $queryBuilder->getQuery()->getArrayResult();
359
    }
360
361
    /**
362
     * Retrieve columns for select.
363
     * Column order_id is used to generate average report
364
     * Column without_tax is for unit price without tax in item units
365
     * Columns without_tax_promo, without_tax_shipping, tax columns are respectively for promotions, shipping, tax amounts
366
     * Columns item and total are respectively for total for items total for orders (With shipping etc.)
367
     *
368
     * @param bool $isItemUnit
369
     * @param bool $isItem
370
     * @param bool $isOrder
371
     * @return string
372
     */
373
    protected function getSelectColumns(bool $isItemUnit = false, bool $isItem = false, bool $isOrder = false): string
374
    {
375
        return implode(',',[
376
            // Order ID
377
            'o.id as order_id',
378
379
            // Product infos
380
            ($isItemUnit ? 'IDENTITY(variant.product) as product_id' : ($isItem ? 'IDENTITY(variant.product) as product_id' : '\'\' as product_id')),
381
            ($isItemUnit ? 'item.productName as product_name' : ($isItem ? 'element.productName as product_name' : '\'\' as product_name')),
382
383
            // Variant infos
384
            ($isItemUnit ? 'IDENTITY(item.variant) as variant_id' : ($isItem ? 'IDENTITY(element.variant) as variant_id' : '\'\' as variant_id')),
385
            ($isItemUnit ? 'CONCAT(item.productName, \' \' ,item.variantName) as variant_name' : ($isItem ? 'CONCAT(element.productName, \' \' , element.variantName) as variant_name' : '\'\' as variant_name')),
386
387
            // Adjustments
388
            $isItemUnit ? 'item.unitPrice - COALESCE(tax_adjustment.amount, 0) as without_tax' : '0 as without_tax', // Only retrieve without_tax price for item units
389
            '(COALESCE(order_promotion_adjustment.amount, 0) + COALESCE(order_item_promotion_adjustment.amount, 0) + COALESCE(order_shipping_promotion_adjustment.amount, 0) + COALESCE(order_unit_promotion_adjustment.amount, 0)) AS without_tax_promo',
390
            'shipping_adjustment.amount as without_tax_shipping',
391
            'tax_adjustment.amount as tax',
392
393
            // Totals
394
            $isOrder ? 'o.total as total' : '0 as total',
395
            $isItem ? 'element.total as item_row' : '0 as item_row'
396
        ]);
397
    }
398
399
    /**
400
     * Make joins with all adjustments, add conditions and set parameters to query
401
     *
402
     * @param QueryBuilder $queryBuilder
403
     * @param ChannelInterface $channel
404
     * @param \DateTimeInterface $from
405
     * @param \DateTimeInterface $to
406
     * @param bool $isOrder
407
     * @return mixed
408
     */
409
    protected function appendAdjustmentsAndParameters(
410
        QueryBuilder $queryBuilder,
411
        ChannelInterface $channel,
412
        \DateTimeInterface $from,
413
        \DateTimeInterface $to,
414
        bool $isOrder = false
415
    ) {
416
        $elementAlias = $isOrder ? 'o' : 'element';
417
        return $queryBuilder
418
            // Adjustments joins
419
            ->leftJoin($elementAlias . '.adjustments', 'tax_adjustment', 'WITH', 'tax_adjustment.type = :tax_type')
420
            ->leftJoin($elementAlias . '.adjustments', 'shipping_adjustment', 'WITH', 'shipping_adjustment.type = :shipping_type')
421
            ->leftJoin($elementAlias . '.adjustments', 'order_promotion_adjustment', 'WITH', 'order_promotion_adjustment.type = :order_promotion_type OR order_promotion_adjustment.type = :admin_order_promotion_type')
422
            ->leftJoin($elementAlias . '.adjustments', 'order_item_promotion_adjustment', 'WITH', 'order_item_promotion_adjustment.type = :order_item_promotion_type OR order_item_promotion_adjustment.type = :admin_order_item_promotion_type')
423
            ->leftJoin($elementAlias . '.adjustments', 'order_shipping_promotion_adjustment', 'WITH', 'order_shipping_promotion_adjustment.type = :order_shipping_promotion_type')
424
            ->leftJoin($elementAlias . '.adjustments', 'order_unit_promotion_adjustment', 'WITH', 'order_unit_promotion_adjustment.type = :order_unit_promotion_type')
425
            // Adjustments parameters
426
            ->setParameter('tax_type', AdjustmentInterface::TAX_ADJUSTMENT)
427
            ->setParameter('shipping_type', AdjustmentInterface::SHIPPING_ADJUSTMENT)
428
            ->setParameter('order_promotion_type', AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT)
429
            ->setParameter('admin_order_promotion_type', self::ADMIN_ORDER_DISCOUNT_ADJUSTMENT)
430
            ->setParameter('order_item_promotion_type', AdjustmentInterface::ORDER_ITEM_PROMOTION_ADJUSTMENT)
431
            ->setParameter('admin_order_item_promotion_type', self::ADMIN_ORDER_ITEM_DISCOUNT_ADJUSTMENT)
432
            ->setParameter('order_shipping_promotion_type', AdjustmentInterface::ORDER_SHIPPING_PROMOTION_ADJUSTMENT)
433
            ->setParameter('order_unit_promotion_type', AdjustmentInterface::ORDER_UNIT_PROMOTION_ADJUSTMENT)
434
            // Filters on orders in channel, which are paid, not refunded and completed between the wanted dates
435
            ->andWhere('o.channel = :channel')
436
            ->andWhere('o.state = :state')
437
            ->andWhere('o.paymentState != :payment_state')
438
            ->andWhere('o.checkoutCompletedAt BETWEEN :from AND :to')
439
            // Filters parameters
440
            ->setParameter('channel', $channel)
441
            ->setParameter('state', OrderInterface::STATE_FULFILLED)
442
            ->setParameter('payment_state', PaymentInterface::STATE_REFUNDED)
443
            ->setParameter('from', $from)
444
            ->setParameter('to', $to)
445
        ;
446
    }
447
448
    /**
449
     * Populate result array with options and option values data
450
     *
451
     * @param string $localeCode
452
     * @return array
453
     */
454
    protected function populateOptions(string $localeCode): array
455
    {
456
        $variantOptions = $this->getVariantsOptions($localeCode);
457
        $salesResults = [];
458
459
        foreach($this->results as $result) {
460
            $variantId = $result['variant_id'];
461
            $options = $variantOptions[$variantId];
462
463
            // Rename field with _total
464
            foreach ($result as $key => $value) {
465
                if (strpos($key, '_total')) {
466
                    $result[str_replace('_total', '', $key)] = $value;
467
                    unset($result[$key]);
468
                }
469
            }
470
            foreach ($options as $optionCode => $option) {
471
                $result['option_code'] = $optionCode;
472
                $result['option_label'] = $option['label'];
473
                $result['option_value_code'] = $option['value_code'];
474
                $result['option_value_label'] = $option['value_label'];
475
                $salesResults[] = $result;
476
            }
477
        }
478
479
        return $salesResults;
480
    }
481
482
    /**
483
     * Retrieve options for all variants and build an array
484
     *
485
     * @param string $localeCode
486
     * @return array
487
     */
488
    protected function getVariantsOptions(string $localeCode): array
489
    {
490
        $queryBuilder = $this->createProductVariantQuery()
491
            ->select('v.id AS variant_id, option.code AS option_code, option_translation.name AS option_label, option_value.code AS option_value_code, option_value_translation.value AS option_value_label')
492
            ->leftJoin('v.optionValues', 'option_value')
493
            ->leftJoin('option_value.translations', 'option_value_translation', 'WITH', 'option_value_translation.locale = :locale')
494
            ->leftJoin('option_value.option', 'option')
495
            ->leftJoin('option.translations', 'option_translation', 'WITH', 'option_translation.locale = :locale')
496
            ->setParameter('locale', $localeCode)
497
        ;
498
499
        $variantOptionsValues = [];
500
501
        $result = $queryBuilder->getQuery()->getArrayResult();
502
        foreach ($result as $variantOptionValue) {
503
            $variantOptionsValues[$variantOptionValue['variant_id']][$variantOptionValue['option_code']] = [
504
                'label' => $variantOptionValue['option_label'],
505
                'value_code' => $variantOptionValue['option_value_code'],
506
                'value_label' => $variantOptionValue['option_value_label'],
507
            ];
508
        }
509
510
        return $variantOptionsValues;
511
    }
512
513
    /**
514
     * Init the result with 0 totals
515
     */
516
    protected function initResult()
517
    {
518
        $this->result = [
519
            'without_tax_total' => 0,
520
            'without_tax_promo_total' => 0,
521
            'without_tax_shipping_total' => 0,
522
            'tax_total' => 0,
523
            'total' => 0,
524
            'item_row_total' => 0,
525
        ];
526
        $this->elements = [];
527
    }
528
529
    /**
530
     * Increment results with given array
531
     *
532
     * @param array $elementResults
533
     * @param string|null $groupField
534
     */
535
    protected function addResults(array $elementResults, ?string $groupField = null): void
536
    {
537
        // Loop on given elements to increments current result
538
        foreach ($elementResults as $elementResult) {
539
            foreach ($this->result as $key => $val) {
540
                if (strpos($key, 'total') !== false) {
541
                    // Get the field key, for example `without_tax_shipping` if we need to increment `without_tax_shipping_total`
542
                    $resultKey = str_replace('_total', '', $key);
543
                    if (isset($elementResult[$resultKey])) {
544
                        $this->result[$key] += (int) $elementResult[$resultKey]; // Cast in int because doctrine return string for columns with `+`, and we can have null values
545
                    }
546
                }
547
            }
548
            // Add group field value if we got one, for example, an order ID to have an average per order or a list per product variant
549
            if ($groupField !== null) {
550
                $this->elements[$elementResult[$groupField]] = $elementResult[$groupField];
551
            }
552
        }
553
    }
554
555
    /**
556
     * Make the sum of results by elements
557
     *
558
     * @param array $elementResults
559
     * @param string $groupField
560
     * @param string|null $labelField
561
     * @param array|null $extraFields
562
     */
563
    protected function addResultsByElement(array $elementResults, string $groupField, ?string $labelField = null, ?array $extraFields = null): void
564
    {
565
        // Loop on given elements to increments current result
566
        foreach ($elementResults as $elementResult) {
567
            $elementId = $elementResult[$groupField];
568
            // Init results for this element or retrieve existing one
569
            if (!isset($this->results[$elementId])) {
570
                $this->initResult();
571
            } else {
572
                $this->result = $this->results[$elementId];
573
                $this->elements = $this->elementsArray[$elementId];
574
            }
575
            // Add results by order
576
            $this->addResults([$elementResult], 'order_id');
577
            $this->result['order_id'] = $elementResult['order_id'];
578
579
            // Add extra fields
580
            $this->result[$groupField] = $elementId; // Grouped field ID
581
            if ($labelField && isset($elementResult[$labelField]) && !empty($elementResult[$labelField])) {
582
                $this->result[$labelField] = $elementResult[$labelField]; // Grouped field label if given
583
            } elseif($labelField && (!isset($elementResult[$labelField]) || empty($elementResult[$labelField]))) {
584
                $this->result[$labelField] = '';
585
            }
586
            if (!empty($extraFields)) {
587
                foreach ($extraFields as $extraField) {
588
                    if (isset($elementResult[$extraField])) {
589
                        $this->result[$extraField] = $elementResult[$extraField];
590
                    } else {
591
                        $this->result[$extraField] = '';
592
                    }
593
                }
594
            }
595
596
            // Update results for this element
597
            $this->results[$elementId] = $this->result;
598
            $this->elementsArray[$elementId] = $this->elements;
599
        }
600
601
        // Aggregate number of order per element
602
        foreach ($this->results as $key => $value) {
603
            $this->results[$key]['number_of_elements'] = count($this->elementsArray[$key]);
604
        }
605
    }
606
607
    /**
608
     * Make the average of results depending on number of elements
609
     */
610
    protected function averageResult(): void
611
    {
612
        if (!empty($this->elements)) {
613
            $numberOfElements = count($this->elements);
614
            foreach ($this->result as $key => $val) {
615
                $this->result[$key] = round($this->result[$key] / $numberOfElements);
616
            }
617
            $this->result['number_of_elements'] = count($this->elements);
618
        } else {
619
            $this->result['number_of_elements'] = 0;
620
        }
621
    }
622
623
    /**
624
     * @return QueryBuilder
625
     */
626
    private function createOrderQuery()
627
    {
628
        /** @var EntityRepository $repository */
629
        $repository = $this->orderRepository;
630
        return $repository->createQueryBuilder('o');
631
    }
632
633
    /**
634
     * @return QueryBuilder
635
     */
636
    private function createProductVariantQuery()
637
    {
638
        /** @var EntityRepository $repository */
639
        $repository = $this->productVariantRepository;
640
        return $repository->createQueryBuilder('v');
641
    }
642
}
643