Issues (188)

src/Parser/MultiKeyCollection.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\ORM\Parser;
6
7
use Cycle\ORM\Exception\ParserException;
8
use Cycle\Database\Injection\Parameter;
9
10
/**
11
 * @internal
12
 */
13
final class MultiKeyCollection
14
{
15
    /**
16
     * [Index] = [key1, key2, ...]
17
     *
18
     * @var string[][]
19
     */
20
    private array $indexes = [];
21
22
    /**
23
     * [Index][key1-value][key2-value][...] = [ITEM1, ITEM2, ...].
24
     */
25
    private array $data = [];
26
27
    /**
28
     * Contains key values of last added item for each Index
29
     * [index] = [key1-value, key2-value, ...]
30
     */
31
    private array $lastItemKeys = [];
32
33 4114
    public function createIndex(string $name, array $keys): void
34
    {
35 4114
        $this->indexes[$name] = $keys;
36 4114
        $this->data[$name] = [];
37
    }
38
39 6308
    public function getIndexes(): array
40
    {
41 6308
        return \array_keys($this->indexes);
42
    }
43
44 4114
    public function hasIndex(string $index): bool
45
    {
46 4114
        return \array_key_exists($index, $this->indexes);
47
    }
48
49 3034
    public function getItemsCount(string $index, array $values): int
50
    {
51
        try {
52 3034
            return \count($this->getValues($this->data[$index], $values));
53 2
        } catch (\Exception) {
54 2
            return 0;
55
        }
56
    }
57
58 4014
    public function getItemsSubset(string $index, array $values): array
59
    {
60 4014
        return $this->getValues($this->data[$index], $values);
61
    }
62
63 264
    public function getLastItemKeys(string $index): array
64
    {
65 264
        return $this->lastItemKeys[$index];
66
    }
67
68 4072
    public function addItem(string $index, array &$data): void
69
    {
70 4072
        $pool = &$this->data[$index];
71 4072
        $itemKeys = [];
72 4072
        foreach ($this->indexes[$index] as $key) {
73 4072
            $keyValue = $data[$key] ?? null;
74 4072
            if (!\is_scalar($keyValue)) {
75 336
                throw new \InvalidArgumentException("Invalid value on the key `$key`.");
76
            }
77 4072
            $itemKeys[] = $keyValue;
78 4072
            if (!\array_key_exists($keyValue, $pool)) {
79 4072
                $pool[$keyValue] = [];
80
            }
81 4072
            $pool = &$pool[$keyValue];
82
        }
83 4072
        $pool[] = &$data;
84 4072
        $this->lastItemKeys[$index] = $itemKeys;
85
    }
86
87 2380
    public function getCriteria(string $index, bool $useParameter = false): array
0 ignored issues
show
The parameter $useParameter is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

87
    public function getCriteria(string $index, /** @scrutinizer ignore-unused */ bool $useParameter = false): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
88
    {
89 2380
        $result = [];
90 2380
        foreach ($this->data[$index] as $key => $data) {
91 2340
            $base = [$this->indexes[$index][0] => $key];
92 2340
            $result[] = $this->extractAssoc($data, $base, $this->indexes[$index], 1, true);
93
        }
94 2380
        return $result === [] ? [] : \array_merge(...$result);
95
    }
96
97
    public function getItems(string $indexName): \Generator
98
    {
99
        $depth = \count($this->indexes[$indexName]);
100
101
        $iterator = static function (array $data, $deep) use (&$depth, &$iterator) {
102
            if ($deep < $depth) {
103
                ++$deep;
104
                foreach ($data as $subset) {
105
                    yield from $iterator($subset, $deep);
106
                }
107
                return;
108
            }
109
            yield from $data;
110
        };
111
        yield from $iterator($this->data[$indexName], 1);
112
    }
113
114
    /**
115
     * @param string[] $keys
116
     */
117 2340
    private function extractAssoc(array $data, array $base, array $keys, int $level, bool $useParameter): array
118
    {
119
        // Optimization. Group last column values into single Parameter.
120
        // For example, where condition will look like:
121
        // key1="1" AND key2 IN (1, 2, 3)
122
        // instead of:
123
        // (key1="1" AND key2="1") OR (key1="1" AND key2="2") OR (key1="1" AND key2="3")
124 2340
        if ($useParameter && $level === \count($keys) - 1 && \count($data) > 1) {
125 96
            return [$base + [$keys[$level] => new Parameter(\array_keys($data))]];
126
        }
127
128 2244
        if ($level >= \count($keys)) {
129 2244
            return [$base];
130
        }
131 168
        $result = [];
132 168
        $field = $keys[$level];
133 168
        foreach ($data as $key => $value) {
134 168
            $base[$field] = $key;
135 168
            $result[] = $this->extractAssoc($value, $base, $keys, $level + 1, $useParameter);
136
        }
137 168
        return \array_merge(...$result);
138
    }
139
140 4014
    private function getValues(array &$dataSet, array $keys): array
141
    {
142 4014
        $value = &$dataSet;
143 4014
        foreach ($keys as $key) {
144 4014
            if (!\array_key_exists($key, $value)) {
145 4
                throw new ParserException('Value not found.');
146
            }
147 4014
            $value = &$value[$key];
148
        }
149 4014
        return $value;
150
    }
151
}
152