Completed
Branch feature/pre-split (e5594a)
by Anton
03:09
created

AggregationHelper   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 130
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 130
rs 10
c 0
b 0
f 0
wmc 13
lcom 1
cbo 5

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
B createAggregation() 0 26 3
A configureSelector() 0 16 2
C findValue() 0 32 7
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ODM\Helpers;
8
9
use Spiral\Models\AccessorInterface;
10
use Spiral\Models\EntityInterface;
11
use Spiral\ODM\CompositableInterface;
12
use Spiral\ODM\DocumentEntity;
13
use Spiral\ODM\Entities\DocumentSelector;
14
use Spiral\ODM\Exceptions\AggregationException;
15
use Spiral\ODM\ODMInterface;
16
use Spiral\ODM\Schemas\Definitions\AggregationDefinition;
17
18
/**
19
 * Provides ability to configure ODM Selector based on values and query template provided by source
20
 * model.
21
 *
22
 * @todo add sorts and limits
23
 * @see  AggregationDefinition
24
 */
25
class AggregationHelper
26
{
27
    /**
28
     * @var DocumentEntity
29
     */
30
    private $source;
31
32
    /**
33
     * @var array
34
     */
35
    private $schema = [];
36
37
    /**
38
     * @var ODMInterface
39
     */
40
    protected $odm;
41
42
    /**
43
     * @param DocumentEntity $source
44
     * @param array          $schema DocumentEntity schema.
45
     * @param ODMInterface   $odm
46
     */
47
    public function __construct(DocumentEntity $source, array $schema, ODMInterface $odm)
48
    {
49
        $this->source = $source;
50
        $this->schema = $schema;
51
        $this->odm = $odm;
52
    }
53
54
    /**
55
     * Create aggregation (one or many) based on given source values.
56
     *
57
     * @param string $aggregation
58
     *
59
     * @return DocumentSelector|CompositableInterface
60
     *
61
     * @throws AggregationException
62
     */
63
    public function createAggregation(string $aggregation)
64
    {
65
        if (!isset($this->schema[DocumentEntity::SH_AGGREGATIONS][$aggregation])) {
66
            throw new AggregationException(
67
                "Undefined aggregation '{$aggregation}' in '" . get_class($this->source) . "'"
68
            );
69
        }
70
71
        $schema = $this->schema[DocumentEntity::SH_AGGREGATIONS][$aggregation];
72
73
        //Let's create selector
74
        $selector = new DocumentSelector(
75
            $this->odm->collection($schema[1]),
76
            $schema[1],
77
            $this->odm
78
        );
79
80
        //Ensure selection query
81
        $selector = $this->configureSelector($selector, $schema);
82
83
        if ($schema[0] == DocumentEntity::ONE) {
84
            return $selector->findOne();
85
        }
86
87
        return $selector;
88
    }
89
90
    /**
91
     * Configure DocumentSelector using aggregation schema.
92
     *
93
     * @param DocumentSelector $selector
94
     * @param array            $aggregation
95
     *
96
     * @return DocumentSelector
97
     */
98
    protected function configureSelector(
99
        DocumentSelector $selector,
100
        array $aggregation
101
    ): DocumentSelector {
102
        //@see AggregationDefinition
103
        $query = $aggregation[2];
104
105
        //Mounting selection values
106
        array_walk_recursive($query, function (&$value) {
107
            if (strpos($value, 'self::') === 0) {
108
                $value = $this->findValue(substr($value, 6));
109
            }
110
        });
111
112
        return $selector->where($query);
113
    }
114
115
    /**
116
     * Find field value using dot notation.
117
     *
118
     * @param string $name
119
     *
120
     * @return mixed|null
121
     */
122
    private function findValue(string $name)
123
    {
124
        $source = $this->source;
125
126
        $path = explode('.', $name);
127
        foreach ($path as $step) {
128
            if ($source instanceof EntityInterface) {
129
                if (!$source->hasField($step)) {
130
                    return null;
131
                }
132
133
                //Sub entity or field
134
                $source = $source->getField($step);
135
                continue;
136
            }
137
138
            if ($source instanceof AccessorInterface) {
139
                $source = $source->packValue();
140
                continue;
141
            }
142
143
            if (is_array($source) && array_key_exists($step, $source)) {
144
                $source = &$source[$step];
145
                continue;
146
            }
147
148
            //Unable to resolve value, an exception required here
149
            return null;
150
        }
151
152
        return $source;
153
    }
154
}