Test Failed
Push — develop ( cf0157...996c4f )
by Nuno
05:01
created

LocalFactory   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 239
Duplicated Lines 0 %

Test Coverage

Coverage 21.82%

Importance

Changes 0
Metric Value
eloc 89
dl 0
loc 239
ccs 12
cts 55
cp 0.2182
rs 10
c 0
b 0
f 0
wmc 28

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A isUnretrievableAttributes() 0 3 2
A isDisableTypoToleranceOnAttributes() 0 3 2
A isAttributesForFaceting() 0 3 1
A getAttributes() 0 22 5
F create() 0 45 12
A isCustomRanking() 0 3 1
A isSearchableAttributes() 0 5 4
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of Scout Extended.
7
 *
8
 * (c) Algolia Team <[email protected]>
9
 *
10
 *  For the full copyright and license information, please view the LICENSE
11
 *  file that was distributed with this source code.
12
 */
13
14
namespace Algolia\ScoutExtended\Settings;
15
16
use function in_array;
17
use function is_string;
18
use function LogicException;
0 ignored issues
show
introduced by
The function LogicException was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
19
use InvalidArgumentException;
20
use Algolia\AlgoliaSearch\Index;
21
use Illuminate\Database\QueryException;
22
use Algolia\ScoutExtended\Searchable\Aggregator;
23
use Algolia\ScoutExtended\Exceptions\ModelNotFoundException;
24
use Algolia\ScoutExtended\Repositories\RemoteSettingsRepository;
25
use Illuminate\Database\Eloquent\ModelNotFoundException as BaseModelNotFoundException;
26
27
/**
28
 * @internal
29
 */
30
final class LocalFactory
31
{
32
    /**
33
     * @var \Algolia\ScoutExtended\Repositories\RemoteSettingsRepository
34
     */
35
    private $remoteRepository;
36
37
    /**
38
     * @var string[]
39
     */
40
    private static $customRankingKeys = [
41
        '*ed_at',
42
        '*count_*',
43
        '*_count*',
44
    ];
45
46
    /**
47
     * @var string[]
48
     */
49
    private static $unsearchableAttributesKeys = [
50
        'id',
51
        '*_id',
52
        'id_*',
53
        '*ed_at',
54
        'count',
55
        '*_count',
56
        'count_*',
57
        '*image*',
58
        '*url*',
59
        '*link*',
60
        '*password*',
61
        '*token*',
62
        '*hash*',
63
    ];
64
65
    /**
66
     * @var string[]
67
     */
68
    private static $attributesForFacetingKeys = [
69
        '*category*',
70
        '*list*',
71
        '*country*',
72
        '*city*',
73
        '*type*',
74
    ];
75
76
    /**
77
     * @var string[]
78
     */
79
    private static $unretrievableAttributes = [
80
        '*password*',
81
        '*token*',
82
        '*secret*',
83
        '*hash*',
84
    ];
85
86
    /**
87
     * @var string[]
88
     */
89
    private static $unsearchableAttributesValues = [
90
        'http://*',
91
        'https://*',
92
    ];
93
94
    /**
95
     * @var string[]
96
     */
97
    private static $disableTypoToleranceOnAttributesKeys = [
98
        'slug',
99
        '*_slug',
100
        'slug_*',
101
        '*code*',
102
        '*sku*',
103
        '*reference*',
104
    ];
105
106
    /**
107
     * SettingsFactory constructor.
108
     *
109
     * @param \Algolia\ScoutExtended\Repositories\RemoteSettingsRepository $remoteRepository
110
     *
111
     * @return void
112
     */
113 2
    public function __construct(RemoteSettingsRepository $remoteRepository)
114
    {
115 2
        $this->remoteRepository = $remoteRepository;
116 2
    }
117
118
    /**
119
     * Creates settings for the given model.
120
     *
121
     * @param \Algolia\AlgoliaSearch\Index $index
122
     * @param string $model
123
     *
124
     * @return \Algolia\ScoutExtended\Settings\Settings
125
     */
126 2
    public function create(Index $index, string $model): Settings
127
    {
128 2
        $attributes = $this->getAttributes($model);
129
        $searchableAttributes = [];
130
        $attributesForFaceting = [];
131
        $customRanking = [];
132
        $disableTypoToleranceOnAttributes = [];
133
        $unretrievableAttributes = [];
134
        foreach ($attributes as $key => $value) {
135
            $key = (string) $key;
136
137
            if ($this->isSearchableAttributes($key, $value)) {
138
                $searchableAttributes[] = $key;
139
            }
140
141
            if ($this->isAttributesForFaceting($key, $value)) {
142
                $attributesForFaceting[] = $key;
143
            }
144
145
            if ($this->isCustomRanking($key, $value)) {
146
                $customRanking[] = "desc({$key})";
147
            }
148
149
            if ($this->isDisableTypoToleranceOnAttributes($key, $value)) {
150
                $disableTypoToleranceOnAttributes[] = $key;
151
            }
152
153
            if ($this->isUnretrievableAttributes($key, $value)) {
154
                $unretrievableAttributes[] = $key;
155
            }
156
        }
157
158
        $detectedSettings = [
159
            'searchableAttributes' => ! empty($searchableAttributes) ? $searchableAttributes : null,
160
            'attributesForFaceting' => ! empty($attributesForFaceting) ? $attributesForFaceting : null,
161
            'customRanking' => ! empty($customRanking) ? $customRanking : null,
162
            'disableTypoToleranceOnAttributes' => ! empty($disableTypoToleranceOnAttributes) ?
163
                $disableTypoToleranceOnAttributes : null,
164
            'unretrievableAttributes' => ! empty($unretrievableAttributes) ? $unretrievableAttributes : null,
165
            'queryLanguages' => array_unique([config('app.locale'), config('app.fallback_locale')]),
166
        ];
167
168
        $settings = array_merge($this->remoteRepository->find($index)->compiled(), $detectedSettings);
169
170
        return new Settings($settings, $this->remoteRepository->defaults());
171
    }
172
173
    /**
174
     * Checks if the given key/value is a 'searchableAttributes'.
175
     *
176
     * @param  string $key
177
     * @param  mixed $value
178
     *
179
     * @return bool
180
     */
181
    public function isSearchableAttributes(string $key, $value): bool
182
    {
183
        return ! is_object($value) && ! is_array($value) &&
184
            ! str_is(self::$unsearchableAttributesKeys, $key) &&
185
            ! str_is(self::$unsearchableAttributesValues, $value);
186
    }
187
188
    /**
189
     * Checks if the given key/value is a 'attributesForFaceting'.
190
     *
191
     * @param  string $key
192
     * @param  mixed $value
193
     *
194
     * @return bool
195
     */
196
    public function isAttributesForFaceting(string $key, $value): bool
197
    {
198
        return str_is(self::$attributesForFacetingKeys, $key);
199
    }
200
201
    /**
202
     * Checks if the given key/value is a 'customRanking'.
203
     *
204
     * @param  string $key
205
     * @param  mixed $value
206
     *
207
     * @return bool
208
     */
209
    public function isCustomRanking(string $key, $value): bool
210
    {
211
        return str_is(self::$customRankingKeys, $key);
212
    }
213
214
    /**
215
     * Checks if the given key/value is a 'disableTypoToleranceOnAttributes'.
216
     *
217
     * @param  string $key
218
     * @param  mixed $value
219
     *
220
     * @return bool
221
     */
222
    public function isDisableTypoToleranceOnAttributes(string $key, $value): bool
223
    {
224
        return is_string($key) && str_is(self::$disableTypoToleranceOnAttributesKeys, $key);
225
    }
226
227
    /**
228
     * Checks if the given key/value is a 'unretrievableAttributes'.
229
     *
230
     * @param  string $key
231
     * @param  mixed $value
232
     *
233
     * @return bool
234
     */
235
    public function isUnretrievableAttributes(string $key, $value): bool
236
    {
237
        return is_string($key) && str_is(self::$unretrievableAttributes, $key);
238
    }
239
240
    /**
241
     * Tries to get attributes from the searchable class.
242
     *
243
     * @param  string $searchable
244
     *
245
     * @return array
246
     */
247 2
    private function getAttributes(string $searchable): array
248
    {
249 2
        $attributes = [];
250
251 2
        if (in_array(Aggregator::class, class_parents($searchable), true)) {
252
            foreach (($instance = new $searchable)->getModels() as $model) {
0 ignored issues
show
Unused Code introduced by
The assignment to $instance is dead and can be removed.
Loading history...
253
                $attributes = array_merge($attributes, $this->getAttributes($model));
254
            }
255
        } else {
256 2
            $instance = null;
257
258
            try {
259 2
                $instance = $searchable::firstOrFail();
260 2
            } catch (QueryException | BaseModelNotFoundException $e) {
261 2
                throw tap(new ModelNotFoundException())->setModel($searchable);
262
            }
263
264
            $attributes = method_exists($instance, 'toSearchableArray') ? $instance->toSearchableArray() :
265
                $instance->toArray();
266
        }
267
268
        return $attributes;
269
    }
270
}
271