Passed
Push — master ( e8ea42...382c9c )
by Nicolaas
03:44
created

FindClassesAndFields::isValidFieldType()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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