Completed
Push — master ( ee56eb...1eb78b )
by Juuso
04:46
created

AlgoliaManager::getClient()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace leinonen\Yii2Algolia;
4
5
use AlgoliaSearch\Client;
6
use AlgoliaSearch\Index;
7
use leinonen\Yii2Algolia\ActiveRecord\ActiveQueryChunker;
8
use leinonen\Yii2Algolia\ActiveRecord\ActiveRecordFactory;
9
use yii\db\ActiveQuery;
10
11
/**
12
 * @method setConnectTimeout(int $connectTimeout, int $timeout = 30, int $searchTimeout = 5)
13
 * @method enableRateLimitForward(string $adminAPIKey, string $endUserIP, string $rateLimitAPIKey)
14
 * @method setForwarderFor(string $ip)
15
 * @method setAlgoliaUserToken(string $token)
16
 * @method disableRateLimitForward()
17
 * @method isAlive()
18
 * @method setExtraHeader(string $key, string $value)
19
 * @method mixed multipleQueries(array $queries, string $indexNameKey = "indexName", string $strategy = "none")
20
 * @method mixed listIndexes()
21
 * @method deleteIndex(string $indexName)
22
 * @method mixed moveIndex(string $srcIndexName, string $dstIndexName)
23
 * @method mixed copyIndex(string $srcIndexName, string $dstIndexName)
24
 * @method mixed getLogs(int $offset = 0, int $length = 10, string $type = "all")
25
 * @method Index initIndex(string $indexName)
26
 * @method mixed listUserKeys()
27
 * @method mixed getUserKeyACL(string $key)
28
 * @method mixed deleteUserKey(string $key)
29
 * @method mixed addUserKey(array $obj, int $validity = 0, int $maxQueriesPerIPPerHour = 0, int $maxHitsPerQuery = 0, array $indexes = null)
30
 * @method mixed updateUserKey(string $key, array $obj, int $validity = 0, int $maxQueriesPerIPPerHour = 0, int $maxHitsPerQuery = 0, array $indexes = null)
31
 * @method mixed batch(array $requests)
32
 * @method string generateSecuredApiKey(string $privateApiKey, mixed $query, string $userToken = null)
33
 * @method string buildQuery(array $args)
34
 * @method mixed request(\AlgoliaSearch\ClientContext $context, string $method, string $path, array $params, array $data, array $hostsArray, int $connectTimeout, int $readTimeout)
35
 * @method mixed doRequest(\AlgoliaSearch\ClientContext $context, string $method, string $path, array $params, array $data, array $hostsArray, int $connectTimeout, int $readTimeout)
36
 * @method \AlgoliaSearch\PlacesIndex initPlaces(string $appId, string $appKey, array $hostsArray = null, array $options = [])
37
 * @see Client
38
 */
39
class AlgoliaManager
40
{
41
    /**
42
     * @var AlgoliaFactory
43
     */
44
    protected $factory;
45
46
    /**
47
     * @var AlgoliaConfig
48
     */
49
    protected $config;
50
51
    /**
52
     * @var Client
53
     */
54
    protected $client;
55
56
    /**
57
     * @var ActiveRecordFactory
58
     */
59
    protected $activeRecordFactory;
60
61
    /**
62
     * @var null|string
63
     */
64
    protected $env;
65
66
    /**
67
     * @var ActiveQueryChunker
68
     */
69
    private $activeQueryChunker;
70
71
    /**
72
     * Initiates a new AlgoliaManager.
73
     *
74
     * @param Client $client
75
     * @param ActiveRecordFactory $activeRecordFactory
76
     * @param ActiveQueryChunker $activeQueryChunker
77
     */
78 39
    public function __construct(
79
        Client $client,
80
        ActiveRecordFactory $activeRecordFactory,
81
        ActiveQueryChunker $activeQueryChunker
82
    ) {
83 39
        $this->client = $client;
84 39
        $this->activeRecordFactory = $activeRecordFactory;
85 39
        $this->activeQueryChunker = $activeQueryChunker;
86 39
    }
87
88
    /**
89
     * Returns the Algolia Client.
90
     *
91
     * @return Client
92
     */
93 17
    public function getClient()
94
    {
95 17
        return $this->client;
96
    }
97
98
    /**
99
     * Sets the environment for the manager.
100
     *
101
     * @param string $env
102
     */
103 39
    public function setEnv($env)
104
    {
105 39
        $this->env = $env;
106 39
    }
107
108
    /**
109
     * Returns the environment for the manager.
110
     *
111
     * @return null|string
112
     */
113 1
    public function getEnv()
114
    {
115 1
        return $this->env;
116
    }
117
118
    /**
119
     * Indexes a searchable model to all indices.
120
     *
121
     * @param SearchableInterface $searchableModel
122
     *
123
     * @return array
124
     */
125 5
    public function pushToIndices(SearchableInterface $searchableModel)
126
    {
127 5
        $indices = $this->initIndices($searchableModel);
128 5
        $response = [];
129
130 5
        foreach ($indices as $index) {
131 5
            $record = $searchableModel->getAlgoliaRecord();
132 5
            $response[$index->indexName] = $index->addObject($record, $searchableModel->getObjectID());
133 5
        }
134
135 5
        return $response;
136
    }
137
138
    /**
139
     * Indexes multiple searchable models in a batch. The given searchable models must be of the same class.
140
     *
141
     * @param SearchableInterface[] $searchableModels
142
     *
143
     * @return array
144
     */
145 3
    public function pushMultipleToIndices(array $searchableModels)
146
    {
147 3
        $algoliaRecords = $this->getAlgoliaRecordsFromSearchableModelArray($searchableModels);
148 2
        $indices = $this->initIndices($searchableModels[0]);
149
150 2
        $response = [];
151
152 2
        foreach ($indices as $index) {
153
            /* @var Index $index  */
154 2
            $response[$index->indexName] = $index->addObjects($algoliaRecords);
155 2
        }
156
157 2
        return $response;
158
    }
159
160
    /**
161
     * Updates the models data in all indices.
162
     *
163
     * @param SearchableInterface $searchableModel
164
     *
165
     * @return array
166
     */
167 3
    public function updateInIndices(SearchableInterface $searchableModel)
168
    {
169 3
        $indices = $this->initIndices($searchableModel);
170 3
        $response = [];
171
172 3
        foreach ($indices as $index) {
173 3
            $record = $searchableModel->getAlgoliaRecord();
174 3
            $record['objectID'] = $searchableModel->getObjectID();
175 3
            $response[$index->indexName] = $index->saveObject($record);
176 3
        }
177
178 3
        return $response;
179
    }
180
181
    /**
182
     * Updates multiple models data in all indices.  The given searchable models must be of the same class.
183
     *
184
     * @param SearchableInterface[] $searchableModels
185
     *
186
     * @return array
187
     */
188 2
    public function updateMultipleInIndices(array $searchableModels)
189
    {
190 2
        $algoliaRecords = $this->getAlgoliaRecordsFromSearchableModelArray($searchableModels);
191 1
        $indices = $this->initIndices($searchableModels[0]);
192
193 1
        $response = [];
194
195 1
        foreach ($indices as $index) {
196
            /* @var Index $index  */
197 1
            $response[$index->indexName] = $index->saveObjects($algoliaRecords);
198 1
        }
199
200 1
        return $response;
201
    }
202
203
    /**
204
     * Removes a searchable model from indices.
205
     *
206
     * @param SearchableInterface $searchableModel
207
     *
208
     * @return array
209
     * @throws \Exception
210
     */
211 2
    public function removeFromIndices(SearchableInterface $searchableModel)
212
    {
213 2
        $indices = $indices = $this->initIndices($searchableModel);
214 2
        $response = [];
215
216 2
        foreach ($indices as $index) {
217 2
            $objectID = $searchableModel->getObjectID();
218 2
            $response[$index->indexName] = $index->deleteObject($objectID);
219 2
        }
220
221 2
        return $response;
222
    }
223
224
    /**
225
     * Re-indexes the indices safely for the given ActiveRecord Class.
226
     *
227
     * @param string $className The name of the ActiveRecord to be indexed
228
     *
229
     * @return array
230
     */
231 3
    public function reindex($className)
232
    {
233 3
        $this->checkImplementsSearchableInterface($className);
234 2
        $activeRecord = $this->activeRecordFactory->make($className);
235 2
        $indices = $this->initIndices($activeRecord);
0 ignored issues
show
Documentation introduced by
$activeRecord is of type object<yii\db\ActiveRecordInterface>, but the function expects a object<leinonen\Yii2Algolia\SearchableInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
236
237 2
        $records =  $this->activeQueryChunker->chunk(
238 2
            $activeRecord->find(),
239 2
            500,
240
            function ($activeRecordEntities) {
241 2
                return $this->getAlgoliaRecordsFromSearchableModelArray($activeRecordEntities);
242
            }
243 2
        );
244
245 2
        $response = [];
246
247 2
        foreach ($indices as $index) {
248 2
            $temporaryIndexName = 'tmp_' . $index->indexName;
249
250
            /** @var Index $temporaryIndex */
251 2
            $temporaryIndex = $this->initIndex($temporaryIndexName);
252 2
            $temporaryIndex->addObjects($records);
253
254 2
            $settings = $index->getSettings();
255
256
            // Temporary index overrides all the settings on the main one.
257
            // So we need to set the original settings on the temporary one before atomically moving the index.
258 2
            $temporaryIndex->setSettings($settings);
259
260 2
            $response[$index->indexName] = $this->moveIndex($temporaryIndexName, $index->indexName);
261 2
        }
262
263 2
        return $response;
264
    }
265
266
    /**
267
     * Clears the indices for the given Class that implements SearchableInterface.
268
     *
269
     * @param string $className The name of the Class which indices are to be cleared.
270
     *
271
     * @return array
272
     */
273 2
    public function clearIndices($className)
274
    {
275 2
        $this->checkImplementsSearchableInterface($className);
276 1
        $activeRecord = $this->activeRecordFactory->make($className);
277 1
        $response = [];
278
279
        /* @var SearchableInterface $activeRecord */
280 1
        $indices = $indices = $this->initIndices($activeRecord);
281
282 1
        foreach ($indices as $index) {
283 1
            $response[$index->indexName] = $index->clearIndex();
284 1
        }
285
286 1
        return $response;
287
    }
288
289
    /**
290
     * Dynamically pass methods to the Algolia Client.
291
     *
292
     * @param string $method
293
     * @param array $parameters
294
     *
295
     * @return mixed
296
     */
297 14
    public function __call($method, $parameters)
298
    {
299 14
        return call_user_func_array([$this->getClient(), $method], $parameters);
300
    }
301
302
    /**
303
     * Checks if the given class implements SearchableInterface.
304
     *
305
     * @param string $class Either name or instance of the class to be checked.
306
     */
307 5
    private function checkImplementsSearchableInterface($class)
308
    {
309 5
        $reflectionClass = new \ReflectionClass($class);
310
311 5
        if (! $reflectionClass->implementsInterface(SearchableInterface::class)) {
312 2
            throw new \InvalidArgumentException("The class: {$reflectionClass->name} doesn't implement leinonen\\Yii2Algolia\\SearchableInterface");
313
        }
314 3
    }
315
316
    /**
317
     * Initializes indices for the given SearchableModel.
318
     *
319
     * @param SearchableInterface $searchableModel
320
     *
321
     * @return Index[]
322
     */
323 13
    private function initIndices(SearchableInterface $searchableModel)
324
    {
325 13
        $indexNames = $searchableModel->getIndices();
326
327
        $indices = array_map(function ($indexName) {
328 13
            if ($this->env !== null) {
329 3
                $indexName = $this->env . '_' . $indexName;
330 3
            }
331
332 13
            return $this->initIndex($indexName);
333 13
        }, $indexNames);
334
335 13
        return $indices;
336
    }
337
338
    /**
339
     * Maps an array of searchable models into an Algolia friendly array.
340
     *
341
     * @param SearchableInterface[] $searchableModels
342
     *
343
     * @return array
344
     */
345 7
    private function getAlgoliaRecordsFromSearchableModelArray(array $searchableModels)
346
    {
347
        // Use the first element of the array to define what kind of models we are indexing.
348 7
        $arrayType = get_class($searchableModels[0]);
349
350 7
        $algoliaRecords = array_map(function (SearchableInterface $searchableModel) use ($arrayType) {
351 7
            if (! $searchableModel instanceof $arrayType) {
352 2
                throw new \InvalidArgumentException('The given array should not contain multiple different classes');
353
            }
354
355 7
            $algoliaRecord = $searchableModel->getAlgoliaRecord();
356 7
            $algoliaRecord['objectID'] = $searchableModel->getObjectID();
357
358 7
            return $algoliaRecord;
359 7
        }, $searchableModels);
360
361 5
        return $algoliaRecords;
362
    }
363
}
364