Test Failed
Branch develop (dd818d)
by Nuno
03:56
created

UpdateJob   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Test Coverage

Coverage 95.24%

Importance

Changes 0
Metric Value
eloc 60
dl 0
loc 153
ccs 60
cts 63
cp 0.9524
rs 10
c 0
b 0
f 0
wmc 22

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A usesSoftDelete() 0 3 2
B splitSearchable() 0 27 6
A shouldBeSplitted() 0 18 4
B handle() 0 44 9
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\Jobs;
15
16
use function in_array;
17
use function is_array;
18
use function get_class;
19
use function is_object;
20
use function is_string;
21
use Illuminate\Support\Str;
22
use Illuminate\Support\Collection;
23
use Illuminate\Database\Eloquent\Model;
24
use Illuminate\Database\Eloquent\SoftDeletes;
25
use Algolia\AlgoliaSearch\Interfaces\ClientInterface;
26
use Algolia\ScoutExtended\Searchable\ObjectIdEncrypter;
27
28
/**
29
 * @internal
30
 */
31
final class UpdateJob
32
{
33
    /**
34
     * Contains a list of splittables searchables.
35
     *
36
     * Example: [
37
     *      '\App\Thread' => true,
38
     *      '\App\User' => false,
39
     * ];
40
     *
41
     * @var array
42
     */
43
    private $splittables = [];
44
45
    /**
46
     * @var \Illuminate\Support\Collection
47
     */
48
    private $searchables;
49
50
    /**
51
     * UpdateJob constructor.
52
     *
53
     * @param \Illuminate\Support\Collection $searchables
54
     *
55
     * @return void
56
     */
57 12
    public function __construct(Collection $searchables)
58
    {
59 12
        $this->searchables = $searchables;
60 12
    }
61
62
    /**
63
     * @param \Algolia\AlgoliaSearch\Interfaces\ClientInterface $client
64
     *
65
     * @return void
66
     */
67 12
    public function handle(ClientInterface $client): void
68
    {
69 12
        if ($this->searchables->isEmpty()) {
70
            return;
71
        }
72
73 12
        if (config('scout.soft_delete', false) && $this->usesSoftDelete($this->searchables->first())) {
74
            $this->searchables->each->pushSoftDeleteMetadata();
75
        }
76
77 12
        $index = $client->initIndex($this->searchables->first()->searchableAs());
78
79 12
        $objectsToSave = [];
80 12
        $searchablesToDelete = [];
81
82 12
        foreach ($this->searchables as $key => $searchable) {
83 12
            if (empty($array = array_merge($searchable->toSearchableArray(), $searchable->scoutMetadata()))) {
84
                continue;
85
            }
86
87 12
            $array['_tags'] = (array) ($array['_tags'] ?? []);
88
89 12
            array_push($array['_tags'], ObjectIdEncrypter::encrypt($searchable));
90
91 12
            if ($this->shouldBeSplitted($searchable)) {
92 4
                [$pieces, $splittedBy] = $this->splitSearchable($searchable, $array);
93
94 4
                foreach ($pieces as $number => $piece) {
95 4
                    $array['objectID'] = ObjectIdEncrypter::encrypt($searchable, $number);
96 4
                    $array[$splittedBy] = $piece;
97 4
                    $objectsToSave[] = $array;
98
                }
99 4
                $searchablesToDelete[] = $searchable;
100
            } else {
101 8
                $array['objectID'] = ObjectIdEncrypter::encrypt($searchable);
102 12
                $objectsToSave[] = $array;
103
            }
104
        }
105
106 12
        dispatch_now(new DeleteJob(collect($searchablesToDelete)));
107
108 12
        $result = $index->saveObjects($objectsToSave);
109 12
        if (config('scout.synchronous', false)) {
110 1
            $result->wait();
111
        }
112 12
    }
113
114
    /**
115
     * @param  object $searchable
116
     *
117
     * @return bool
118
     */
119 12
    private function shouldBeSplitted($searchable): bool
120
    {
121 12
        $class = get_class($searchable->getModel());
122
123 12
        if (! array_key_exists($class, $this->splittables)) {
124 12
            $this->splittables[$class] = false;
125
126 12
            foreach ($searchable->toSearchableArray() as $key => $value) {
127 12
                $method = 'split'.Str::camel($key);
128 12
                $model = $searchable->getModel();
129 12
                if (method_exists($model, $method)) {
130 4
                    $this->splittables[$class] = true;
131 12
                    break;
132
                }
133
            }
134
        }
135
136 12
        return $this->splittables[$class];
137
    }
138
139
    /**
140
     * @param  object $searchable
141
     * @param  array $array
142
     *
143
     * @return array
144
     */
145 4
    private function splitSearchable($searchable, array $array): array
146
    {
147 4
        $splittedBy = null;
148 4
        $pieces = [];
149 4
        foreach ($array as $key => $value) {
150 4
            $method = 'split'.Str::camel((string) $key);
151 4
            $model = $searchable->getModel();
152 4
            if (method_exists($model, $method)) {
153 4
                $result = $model->{$method}($value);
154
155
                switch (true) {
156 4
                    case is_array($result):
157 2
                        $pieces = $result;
158 2
                        break;
159 2
                    case is_string($result):
160 1
                        $pieces = app($result)($model, $value);
161 1
                        break;
162 1
                    case is_object($result):
163 1
                        $pieces = $result->__invoke($model, $value);
164 1
                        break;
165
                }
166 4
                $splittedBy = $key;
167 4
                break;
168
            }
169
        }
170
171 4
        return [$pieces, $splittedBy];
172
    }
173
174
    /**
175
     * Determine if the given searchable uses soft deletes.
176
     *
177
     * @param  object $searchable
178
     *
179
     * @return bool
180
     */
181 1
    private function usesSoftDelete($searchable): bool
182
    {
183 1
        return $searchable instanceof Model && in_array(SoftDeletes::class, class_uses_recursive($searchable), true);
184
    }
185
}
186