Completed
Push — master ( 995814...b8a5e6 )
by Juuso
12:56 queued 08:00
created

AlgoliaManager::setEnv()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace leinonen\Yii2Algolia;
4
5
use AlgoliaSearch\Client;
6
use AlgoliaSearch\Index;
7
use leinonen\Yii2Algolia\ActiveRecord\ActiveRecordFactory;
8
9
/**
10
 * @method setConnectTimeout(int $connectTimeout, int $timeout = 30, int $searchTimeout = 5)
11
 * @method enableRateLimitForward(string $adminAPIKey, string $endUserIP, string $rateLimitAPIKey)
12
 * @method setForwarderFor(string $ip)
13
 * @method setAlgoliaUserToken(string $token)
14
 * @method disableRateLimitForward()
15
 * @method isAlive()
16
 * @method setExtraHeader(string $key, string $value)
17
 * @method mixed multipleQueries(array $queries, string $indexNameKey = "indexName", string $strategy = "none")
18
 * @method mixed listIndexes()
19
 * @method deleteIndex(string $indexName)
20
 * @method mixed moveIndex(string $srcIndexName, string $dstIndexName)
21
 * @method mixed copyIndex(string $srcIndexName, string $dstIndexName)
22
 * @method mixed getLogs(int $offset = 0, int $length = 10, string $type = "all")
23
 * @method Index initIndex(string $indexName)
24
 * @method mixed listUserKeys()
25
 * @method mixed getUserKeyACL(string $key)
26
 * @method mixed deleteUserKey(string $key)
27
 * @method mixed addUserKey(array $obj, int $validity = 0, int $maxQueriesPerIPPerHour = 0, int $maxHitsPerQuery = 0, array $indexes = null)
28
 * @method mixed updateUserKey(string $key, array $obj, int $validity = 0, int $maxQueriesPerIPPerHour = 0, int $maxHitsPerQuery = 0, array $indexes = null)
29
 * @method mixed batch(array $requests)
30
 * @method string generateSecuredApiKey(string $privateApiKey, mixed $query, string $userToken = null)
31
 * @method string buildQuery(array $args)
32
 * @method mixed request(\AlgoliaSearch\ClientContext $context, string $method, string $path, array $params, array $data, array $hostsArray, int $connectTimeout, int $readTimeout)
33
 * @method mixed doRequest(\AlgoliaSearch\ClientContext $context, string $method, string $path, array $params, array $data, array $hostsArray, int $connectTimeout, int $readTimeout)
34
 * @method \AlgoliaSearch\PlacesIndex initPlaces(string $appId, string $appKey, array $hostsArray = null, array $options = [])
35
 * @see Client
36
 */
37
class AlgoliaManager
38
{
39
    /**
40
     * @var AlgoliaFactory
41
     */
42
    protected $factory;
43
44
    /**
45
     * @var AlgoliaConfig
46
     */
47
    protected $config;
48
49
    /**
50
     * @var null|Client
51
     */
52
    protected $client;
53
54
    /**
55
     * @var ActiveRecordFactory
56
     */
57
    protected $activeRecordFactory;
58
59
    /**
60
     * @var null|string
61
     */
62
    protected $env;
63
64
    /**
65
     * Initiates a new AlgoliaManager.
66
     *
67
     * @param Client $client
68
     * @param ActiveRecordFactory $activeRecordFactory
69
     *
70
     */
71 39
    public function __construct(Client $client, ActiveRecordFactory $activeRecordFactory)
72
    {
73 39
        $this->client = $client;
74 39
        $this->activeRecordFactory = $activeRecordFactory;
75 39
    }
76
77
    /**
78
     * Returns the Algolia Client.
79
     *
80
     * @return Client
0 ignored issues
show
Documentation introduced by
Should the return type not be Client|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
81
     */
82 19
    public function getClient()
83
    {
84 19
        return $this->client;
85
    }
86
87
    /**
88
     * Sets the environment for the manager.
89
     *
90
     * @param string $env
91
     */
92 39
    public function setEnv($env)
93
    {
94 39
        $this->env = $env;
95 39
    }
96
97
    /**
98
     * Returns the environment for the manager.
99
     *
100
     * @return null|string
101
     */
102 1
    public function getEnv()
103
    {
104 1
        return $this->env;
105
    }
106
107
    /**
108
     * Indexes a searchable model to all indices.
109
     *
110
     * @param SearchableInterface $searchableModel
111
     *
112
     * @return array
113
     */
114 5
    public function pushToIndices(SearchableInterface $searchableModel)
115
    {
116 5
        $indices = $this->initIndices($searchableModel);
117 5
        $response = [];
118
119 5
        foreach ($indices as $index) {
120 5
            $record = $searchableModel->getAlgoliaRecord();
121 5
            $response[$index->indexName] = $index->addObject($record, $searchableModel->getObjectID());
122 5
        }
123
124 5
        return $response;
125
    }
126
127
    /**
128
     * Indexes multiple searchable models in a batch. The given searchable models must be of the same class.
129
     *
130
     * @param SearchableInterface[] $searchableModels
131
     *
132
     * @return array
133
     */
134 3
    public function pushMultipleToIndices(array $searchableModels)
135
    {
136 3
        list($indices, $algoliaRecords) = $this->getIndicesAndAlgoliaRecordsFromSearchableModelArray($searchableModels);
137 2
        $response = [];
138
139 2
        foreach ($indices as $index) {
140
            /* @var Index $index  */
141 2
            $response[$index->indexName] = $index->addObjects($algoliaRecords);
142 2
        }
143
144 2
        return $response;
145
    }
146
147
    /**
148
     * Updates the models data in all indices.
149
     *
150
     * @param SearchableInterface $searchableModel
151
     *
152
     * @return array
153
     */
154 3
    public function updateInIndices(SearchableInterface $searchableModel)
155
    {
156 3
        $indices = $this->initIndices($searchableModel);
157 3
        $response = [];
158
159 3
        foreach ($indices as $index) {
160 3
            $record = $searchableModel->getAlgoliaRecord();
161 3
            $record['objectID'] = $searchableModel->getObjectID();
162 3
            $response[$index->indexName] = $index->saveObject($record);
163 3
        }
164
165 3
        return $response;
166
    }
167
168
    /**
169
     * Updates multiple models data in all indices.  The given searchable models must be of the same class.
170
     *
171
     * @param SearchableInterface[] $searchableModels
172
     *
173
     * @return array
174
     */
175 2
    public function updateMultipleInIndices(array $searchableModels)
176
    {
177 2
        list($indices, $algoliaRecords) = $this->getIndicesAndAlgoliaRecordsFromSearchableModelArray($searchableModels);
178 1
        $response = [];
179
180 1
        foreach ($indices as $index) {
181
            /* @var Index $index  */
182 1
            $response[$index->indexName] = $index->saveObjects($algoliaRecords);
183 1
        }
184
185 1
        return $response;
186
    }
187
188
    /**
189
     * Removes a searchable model from indices.
190
     *
191
     * @param SearchableInterface $searchableModel
192
     *
193
     * @return array
194
     * @throws \Exception
195
     */
196 2
    public function removeFromIndices(SearchableInterface $searchableModel)
197
    {
198 2
        $indices = $indices = $this->initIndices($searchableModel);
199 2
        $response = [];
200
201 2
        foreach ($indices as $index) {
202 2
            $objectID = $searchableModel->getObjectID();
203 2
            $response[$index->indexName] = $index->deleteObject($objectID);
204 2
        }
205
206 2
        return $response;
207
    }
208
209
    /**
210
     * Re-indexes the indices safely for the given ActiveRecord Class.
211
     *
212
     * @param string $className The name of the ActiveRecord to be indexed.
213
     *
214
     * @return array
215
     */
216 3
    public function reindex($className)
217
    {
218 3
        $this->checkImplementsSearchableInterface($className);
219 2
        $activeRecord = $this->activeRecordFactory->make($className);
220 2
        $response = [];
221
222
        /** @var SearchableInterface[] $activeRecordEntities */
223 2
        $activeRecordEntities = $activeRecord->find()->all();
224
225
        /* @var SearchableInterface $activeRecord */
226 2
        $indices = $indices = $this->initIndices($activeRecord);
227 2
        $records = [];
228
229 2
        foreach ($activeRecordEntities as $activeRecordEntity) {
230 2
            $record = $activeRecordEntity->getAlgoliaRecord();
231 2
            $record['objectID'] = $activeRecordEntity->getObjectID();
232 2
            $records[] = $record;
233 2
        }
234
235 2
        foreach ($indices as $index) {
236 2
            $temporaryIndexName = 'tmp_' . $index->indexName;
237
238
            /** @var Index $temporaryIndex */
239 2
            $temporaryIndex = $this->initIndex($temporaryIndexName);
240 2
            $temporaryIndex->addObjects($records);
241
242 2
            $settings = $index->getSettings();
243
244
            // Temporary index overrides all the settings on the main one.
245
            // So let's set the original settings on the temporary one before atomically moving the index.
246 2
            $temporaryIndex->setSettings($settings);
247
248 2
            $response[$index->indexName] = $this->moveIndex($temporaryIndexName, $index->indexName);
249 2
        }
250
251 2
        return $response;
252
    }
253
254
    /**
255
     * Clears the indices for the given Class that implements SearchableInterface.
256
     *
257
     * @param string $className The name of the Class which indices are to be cleared.
258
     *
259
     * @return array
260
     */
261 2
    public function clearIndices($className)
262
    {
263 2
        $this->checkImplementsSearchableInterface($className);
264 1
        $activeRecord = $this->activeRecordFactory->make($className);
265 1
        $response = [];
266
267
        /* @var SearchableInterface $activeRecord */
268 1
        $indices = $indices = $this->initIndices($activeRecord);
269
270 1
        foreach ($indices as $index) {
271 1
            $response[$index->indexName] = $index->clearIndex();
272 1
        }
273
274 1
        return $response;
275
    }
276
277
    /**
278
     * Dynamically pass methods to the Algolia Client.
279
     *
280
     * @param string $method
281
     * @param array $parameters
282
     *
283
     * @return mixed
284
     */
285 16
    public function __call($method, $parameters)
286
    {
287 16
        return call_user_func_array([$this->getClient(), $method], $parameters);
288
    }
289
290
    /**
291
     * Checks if the given class implements SearchableInterface.
292
     *
293
     * @param string $class Either name or instance of the class to be checked.
294
     */
295 5
    private function checkImplementsSearchableInterface($class)
296
    {
297 5
        $reflectionClass = new \ReflectionClass($class);
298
299 5
        if (! $reflectionClass->implementsInterface(SearchableInterface::class)) {
300 2
            throw new \InvalidArgumentException("The class: {$reflectionClass->name} doesn't implement leinonen\\Yii2Algolia\\SearchableInterface");
301
        }
302 3
    }
303
304
    /**
305
     * Returns the name of the class for given object.
306
     *
307
     * @param $class
308
     *
309
     * @return string
310
     */
311 5
    private function getClassName($class)
312
    {
313 5
        $reflectionClass = new \ReflectionClass($class);
314
315 5
        return $reflectionClass->name;
316
    }
317
318
    /**
319
     * Initializes indices for the given SearchableModel.
320
     *
321
     * @param SearchableInterface $searchableModel
322
     *
323
     * @return Index[]
324
     */
325 15
    private function initIndices(SearchableInterface $searchableModel)
326
    {
327 15
        $indexNames = $searchableModel->getIndices();
328
329
        $indices = array_map(function ($indexName) {
330 15
            if ($this->env !== null) {
331 3
                $indexName = $this->env . '_' . $indexName;
332 3
            }
333
334 15
            return $this->initIndex($indexName);
335 15
        }, $indexNames);
336
337 15
        return $indices;
338
    }
339
340
    /**
341
     * Maps an array of searchable models into an Algolia friendly array. Returns also indices for the searchable model
342
     * which the array consists of.
343
     *
344
     * @param SearchableInterface[] $searchableModels
345
     *
346
     * @return array
347
     */
348 5
    private function getIndicesAndAlgoliaRecordsFromSearchableModelArray(array $searchableModels)
349
    {
350
        // Use the first element of the array to define what kind of models we are indexing.
351 5
        $arrayType = $this->getClassName($searchableModels[0]);
352 5
        $indices = $this->initIndices($searchableModels[0]);
353
354 5
        $algoliaRecords = array_map(function (SearchableInterface $searchableModel) use ($arrayType) {
355 5
            if (! $searchableModel instanceof $arrayType) {
356 2
                throw new \InvalidArgumentException('The given array should not contain multiple different classes');
357
            }
358
359 5
            $algoliaRecord = $searchableModel->getAlgoliaRecord();
360 5
            $algoliaRecord['objectID'] = $searchableModel->getObjectID();
361
362 5
            return $algoliaRecord;
363 5
        }, $searchableModels);
364
365 3
        return [$indices, $algoliaRecords];
366
    }
367
}
368