FindClassesAndFields   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 75
c 2
b 0
f 0
dl 0
loc 187
rs 9.92
wmc 31

9 Methods

Rating   Name   Duplication   Size   Complexity  
A setBaseClass() 0 5 1
A saveCache() 0 5 1
A getAllDataObjects() 0 10 2
A initCache() 0 5 1
A getFileCache() 0 3 1
A inst() 0 7 2
A isValidFieldType() 0 12 4
B getAllIndexedFields() 0 33 11
B getAllValidFields() 0 33 8
1
<?php
2
3
namespace Sunnysideup\SiteWideSearch\Helpers;
4
5
use SilverStripe\Core\ClassInfo;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Core\Injector\Injectable;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\ORM\DataObject;
10
use SilverStripe\ORM\FieldType\DBString;
11
12
class FindClassesAndFields
13
{
14
    use Injectable;
15
16
    /**
17
     * @var string
18
     */
19
    private const CACHE_NAME = 'SiteWideSearchApi';
20
21
    private const BASIC_FIELDS = [
22
        'ID' => 'Int',
23
        'Created' => 'DBDatetime',
24
        'LastEdited' => 'DBDatetime',
25
        'ClassName' => 'Varchar',
26
    ];
27
28
    protected $baseClass = DataObject::class;
29
30
    protected $debug = false;
31
32
    /**
33
     * format is as follows:
34
     * ```php
35
     *      [
36
     *          'AllDataObjects' => [
37
     *              'BaseClassUsed' => [
38
     *                  0 => ClassNameA,
39
     *                  1 => ClassNameB,
40
     *              ],
41
     *          ],
42
     *          'AllValidFields' => [
43
     *              'ClassNameA' => [
44
     *                  'FieldA' => 'FieldA'
45
     *              ],
46
     *          ],
47
     *          'AllIndexedFields' => [
48
     *              'ClassNameA' => [
49
     *                  0 => ClassNameA,
50
     *                  1 => ClassNameB,
51
     *              ],
52
     *          ],
53
     *          'AllValidFieldTypes' => [
54
     *              'Varchar(30)' => true,
55
     *              'Boolean' => false,
56
     *          ],
57
     *     ],
58
     * ```
59
     * we use true rather than false to be able to use empty to work out if it has been tested before.
60
     *
61
     * @var array
62
     */
63
    protected $cache = [];
64
65
    public function saveCache(): self
66
    {
67
        $this->getFileCache()->setCacheValues(self::CACHE_NAME . '_' . $this->baseClass, $this->cache);
68
69
        return $this;
70
    }
71
72
    protected function getFileCache()
73
    {
74
        return Injector::inst()->get(Cache::class);
75
    }
76
77
    public function initCache(): self
78
    {
79
        $this->cache = $this->getFileCache()->getCacheValues(self::CACHE_NAME . '_' . $this->baseClass);
80
81
        return $this;
82
    }
83
84
    protected static $singleton;
85
86
    public static function inst(string $baseClass)
87
    {
88
        if (self::$singleton === null) {
89
            self::$singleton = Injector::inst()->get(static::class);
90
        }
91
        self::$singleton->setBaseClass($baseClass);
92
        return self::$singleton;
93
    }
94
95
    public function setBaseClass(string $baseClass): self
96
    {
97
        $this->baseClass = $baseClass;
98
99
        return $this;
100
    }
101
102
    public function getAllDataObjects(): array
103
    {
104
        if (! isset($this->cache['AllDataObjects'][$this->baseClass])) {
105
            $this->cache['AllDataObjects'][$this->baseClass] = array_values(
106
                ClassInfo::subclassesFor($this->baseClass, false)
107
            );
108
            $this->cache['AllDataObjects'][$this->baseClass] = array_unique($this->cache['AllDataObjects'][$this->baseClass]);
109
        }
110
111
        return $this->cache['AllDataObjects'][$this->baseClass];
112
    }
113
114
    public function getAllValidFields(string $className, ?bool $isQuickSearch = false, ?array $includedFields = [], ?array $includedClassFieldCombos = []): array
115
    {
116
        if (! isset($this->cache['AllValidFields'][$className])) {
117
            $this->cache['AllValidFields'][$className] = Config::inst()->get($className, 'db') ?? [];
118
            $this->cache['AllValidFields'][$className] = array_merge(
119
                $this->cache['AllValidFields'][$className],
120
                self::BASIC_FIELDS
121
            );
122
        }
123
        $array = [];
124
        foreach ($this->cache['AllValidFields'][$className] as $name => $type) {
125
            if ($this->isValidFieldType($type, $className, $name)) {
126
                $array[$name] = $name;
127
            } elseif (in_array($name, $includedFields, true)) {
128
                $array[$name] = $name;
129
            }
130
        }
131
        if (isset($includedClassFieldCombos[$className])) {
132
            foreach ($includedClassFieldCombos[$className] as $name) {
133
                $array[$name] = $name;
134
            }
135
        }
136
        // print_r($array);
137
        if ($isQuickSearch === false) {
138
            return $array;
139
        }
140
        $indexedFields = $this->getAllIndexedFields(
141
            $className,
142
            $array
143
        );
144
145
        // echo $className.': '.print_r($v, 1).print_r($indexedFields, 1);
146
        return array_values(array_intersect($array, $indexedFields));
147
    }
148
149
    protected function getAllIndexedFields(string $className, array $dbFields): array
150
    {
151
        if (! isset($this->cache['AllIndexedFields'][$className])) {
152
            $this->cache['AllIndexedFields'][$className] = [];
153
            $indexes = Config::inst()->get($className, 'indexes');
154
            if (is_array($indexes)) {
155
                foreach ($indexes as $key => $field) {
156
                    if (is_array($field)) {
157
                        foreach ($field as $test) {
158
                            if (is_array($test)) {
159
                                if (isset($test['columns'])) {
160
                                    $test = $test['columns'];
161
                                } else {
162
                                    continue;
163
                                }
164
                            }
165
166
                            $testArray = explode(',', $test);
167
                            foreach ($testArray as $testInner) {
168
                                $testInner = trim($testInner);
169
                                if (isset($dbFields[$testInner])) {
170
                                    $this->cache['AllIndexedFields'][$className][$testInner] = $dbFields[$key];
171
                                }
172
                            }
173
                        }
174
                    } elseif(isset($dbFields[$key])) {
175
                        $this->cache['AllIndexedFields'][$className][$key] = $key;
176
                    }
177
                }
178
            }
179
        }
180
181
        return $this->cache['AllIndexedFields'][$className];
182
    }
183
184
    /**
185
     * for a type, it works out if it is a valid field type.
186
     */
187
    protected function isValidFieldType(string $type, string $className, string $fieldName): bool
188
    {
189
        if (! isset($this->cache['AllValidFieldTypes'][$type])) {
190
            $this->cache['AllValidFieldTypes'][$type] = false;
191
            $singleton = Injector::inst()->get($className);
192
            $field = $singleton->dbObject($fieldName);
193
            if ($fieldName !== 'ClassName' && $field instanceof DBString) {
194
                $this->cache['AllValidFieldTypes'][$type] = true;
195
            }
196
        }
197
198
        return $this->cache['AllValidFieldTypes'][$type];
199
    }
200
}
201