AggregationHelper::findValue()   C
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 32
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 17
nc 6
nop 1
1
<?php
2
/**
3
 * Spiral, Core Components
4
 *
5
 * @author Wolfy-J
6
 */
7
8
namespace Spiral\ODM\Helpers;
9
10
use Spiral\Models\AccessorInterface;
11
use Spiral\Models\EntityInterface;
12
use Spiral\ODM\CompositableInterface;
13
use Spiral\ODM\DocumentEntity;
14
use Spiral\ODM\Entities\DocumentSelector;
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
 * @todo implement on query level?
24
 * @see  AggregationDefinition
25
 */
26
class AggregationHelper
27
{
28
    /**
29
     * @var array
30
     */
31
    private $schema = [];
32
33
    /**
34
     * @var DocumentEntity
35
     */
36
    protected $source;
37
38
    /**
39
     * @var ODMInterface
40
     */
41
    protected $odm;
42
43
    /**
44
     * @param DocumentEntity $source
45
     * @param ODMInterface   $odm
46
     */
47
    public function __construct(DocumentEntity $source, ODMInterface $odm)
48
    {
49
        $this->schema = (array)$odm->define(get_class($source), ODMInterface::D_SCHEMA);
50
        $this->source = $source;
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
    public function createAggregation(string $aggregation)
62
    {
63
        $schema = $this->schema[DocumentEntity::SH_AGGREGATIONS][$aggregation];
64
65
        //Let's create selector
66
        $selector = $this->odm->selector($schema[1]);
67
68
        //Ensure selection query
69
        $selector = $this->configureSelector($selector, $schema);
70
71
        if ($schema[0] == DocumentEntity::ONE) {
72
            return $selector->findOne();
73
        }
74
75
        return $selector;
76
    }
77
78
    /**
79
     * Configure DocumentSelector using aggregation schema.
80
     *
81
     * @param DocumentSelector $selector
82
     * @param array            $aggregation
83
     *
84
     * @return DocumentSelector
85
     */
86
    protected function configureSelector(
87
        DocumentSelector $selector,
88
        array $aggregation
89
    ): DocumentSelector {
90
        //@see AggregationDefinition
91
        $query = $aggregation[2];
92
93
        //Mounting selection values
94
        array_walk_recursive($query, function (&$value) {
95
            if (strpos($value, 'self::') === 0) {
96
                $value = $this->findValue(substr($value, 6));
97
            }
98
        });
99
100
        return $selector->where($query);
101
    }
102
103
    /**
104
     * Find field value using dot notation.
105
     *
106
     * @param string $name
107
     *
108
     * @return mixed|null
109
     */
110
    private function findValue(string $name)
111
    {
112
        $source = $this->source;
113
114
        $path = explode('.', $name);
115
        foreach ($path as $step) {
116
            if ($source instanceof EntityInterface) {
117
                if (!$source->hasField($step)) {
118
                    return null;
119
                }
120
121
                //Sub entity or field
122
                $source = $source->getField($step);
123
                continue;
124
            }
125
126
            if ($source instanceof AccessorInterface) {
127
                $source = $source->packValue();
128
                continue;
129
            }
130
131
            if (is_array($source) && array_key_exists($step, $source)) {
132
                $source = &$source[$step];
133
                continue;
134
            }
135
136
            //Unable to resolve value, an exception required here
137
            return null;
138
        }
139
140
        return $source;
141
    }
142
}