Passed
Pull Request — master (#1)
by Maxime
05:14
created

ReportRepository::getVariantsOptions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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