Passed
Branch beta (1b8e35)
by Jon
07:16
created

ImportVocabulary::makeResource()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace App\Jobs;
4
5
use App\Events\ImportFailed;
6
use App\Events\ImportFinished;
7
use App\Models\Concept;
8
use App\Models\ConceptAttribute;
9
use App\Models\Element;
10
use App\Models\ElementAttribute;
11
use App\Models\Import;
12
use Exception;
13
use Illuminate\Bus\Queueable;
14
use Illuminate\Contracts\Queue\ShouldQueue;
15
use Illuminate\Database\Eloquent\Collection;
16
use Illuminate\Database\Eloquent\Model;
17
use Illuminate\Foundation\Bus\Dispatchable;
18
use Illuminate\Queue\InteractsWithQueue;
19
use Illuminate\Queue\SerializesModels;
20
use Illuminate\Support\Facades\DB;
21
use MailThief\Message;
22
23
class ImportVocabulary implements ShouldQueue
24
{
25
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
26
    /** @var Import */
27
    private $import;
28
    /** @var Model */
29
    private $resource;
30
    /** @var Collection */
31
    private $formResourceProps;
32
    /** @var Collection */
33
    private $formLanguageProps;
34
    /** @var string */
35
    private $resourceLang;
36
    /** @var array */
37
    private $updatedStatements;
38
    /** @var array */
39
    private $results;
40
41
    /**
42
     * Create a new job instance.
43
     *
44
     * @param int $importId
45
     */
46
    public function __construct(int $importId)
47
    {
48
        $this->import = Import::find($importId);
49
    }
50
51
    /**
52
     * Execute the job.
53
     *   step through the changeset and change/add the statements
54
     *   each row of the changeset is a resource
55
     *   if the row has an id then we're updating it
56
     *   if there's a statement id for a column, we're updating it
57
     *   if we delete a row, we have to delete all of the statements
58
     *   changing the statements should trigger an addition to the history table for each one
59
     *   update each resource base model with data from the statements.
60
     *
61
     * @return void
62
     * @throws \Throwable
63
     * @throws \Exception
64
     */
65
    public function handle()
66
    {
67
        $timer           = new \DateTime();
68
        $this->setFormproperties();
69
        $this->setLanguage();
70
        $vocabId         = $this->isElementSet() ? $this->import->schema_id : $this->import->vocabulary_id;
71
        $changeset       = $this->import->instructions;
72
        $total_processed = 0;
73
        $added           = 0;
74
        $updated         = 0;
75
        $deleted         = 0;
76
        //each item in the main array is a row. Each item in the statements array is a statement
77
        foreach ($changeset['update'] as $reg_id => $row) {
78
            $this->updatedStatements = [];
79
            //start a transaction
80
            DB::transaction(function () use ($reg_id, $row, &$updated) {
81
                $statements = $this->getStatements($reg_id);
82
                $dirty = false;
83
                foreach ($row as $statement) {
84
                    $old = $statements->find($statement['statement_id']);
85
                    if ($old) {
86
                        if ($statement['new value']) {
87
                            $old->update([
88
                                'object'         => $statement['new value'],
89
                                'last_import_id' => $this->import->id,
90
                                'is_generated'   => false,
91
                            ]);
92
                            $this->addUpdateStatement($statement);
93
                            $dirty = true;
94
                        } else {
95
                            $old->delete();
96
                            $this->addUpdateStatement($statement);
97
                            $dirty = true;
98
                        }
99
                    } else {
100
                        //make a new one
101
                        $existingStatement = $statements->filter(function ($item) use ($statement) {
102
                            /* @var Model $item */
103
                            return $item->profile_property_id == $statement['property_id'] &&
104
                                $item->object == $statement['new value'] &&
105
                                $item->getOriginal('language') === $statement['language'];
106
                        });
107
                        if ($existingStatement->count() === 0) {
108
                            $newStatement = $this->makeStatement($statement);
109
                            $this->resource->statements()->save($newStatement);
0 ignored issues
show
Bug introduced by
The method statements() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

109
                            $this->resource->/** @scrutinizer ignore-call */ 
110
                                             statements()->save($newStatement);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
110
                            $this->addUpdateStatement($statement);
111
                            $dirty = true;
112
                        }
113
                    }
114
                }
115
                if (count($this->updatedStatements)) {
116
                    try {
117
                        $this->resource->updateFromStatements($this->updatedStatements);
118
                    } catch (Exception $e) {
119
                        //log the error
120
                        $this->makeErrorLogEntry($row['*uri']['new value'], $e->getMessage());
121
                        //cancel the transaction
122
                        return false;
123
                    }
124
                }
125
                if ($dirty) {
126
                    $updated++;
127
                }
128
            });
129
            $total_processed++;
130
        }
131
        foreach ($changeset['add'] as $row) {
132
            DB::transaction(function () use ($row, $vocabId, &$added) {
133
                $allStatements = [];
134
                foreach ($row as $statement) {
135
                    $this->addUpdateStatement($statement);
136
                    $allStatements[] = $this->makeStatement($statement);
137
                }
138
                try {
139
                    //this will make a resource with insufficient values...
140
                    $resource = $this->makeResource($vocabId);
141
                    //this will update the resource from the statements and save it...
142
                    $resource->updateFromStatements($this->updatedStatements);
143
                    //this will add the statements...
144
                    $resource->statements()->saveMany($allStatements);
145
                    $this->UpdatePrefLabelId($resource);
146
                } catch (Exception $e) {
147
                    //log the error
148
                    $this->makeErrorLogEntry($row['*uri']['new value'], $e->getMessage());
149
                    //cancel the transaction
150
                    return false;
151
                }
152
153
                $added++;
154
            });
155
            $total_processed++;
156
        }
157
        foreach ($changeset['delete'] as $row) {
158
            //TODO implement this...
159
            //delete the resource
160
            //cascade delete the statements, which should cascade delete the reciprocals
161
            $total_processed++;
162
            $deleted++;
163
        }
164
        $this->setResults('timer', $timer->diff(new \DateTime())->format('%h hours; %i minutes; %s seconds'));
165
        $this->import->results = $this->results;
166
        $this->import->batch->increment('handled_count');
167
        $this->import->total_processed_count = $total_processed;
168
        $this->import->added_count           = $added;
169
        $this->import->updated_count         = $updated;
170
        $this->import->deleted_count         = $deleted;
171
        $this->import->imported_at           = new \DateTime();
172
        $this->import->save();
173
174
        event(new ImportFinished($this->import));
175
    }
176
177
    public function failed(Exception $exception)
178
    {
179
        event(new ImportFailed($this->import, $exception));
180
    }
181
182
    private function addUpdateStatement($statement): void
183
    {
184
        if ($this->formLanguageProps->contains($statement['property_id']) &&
185
            $this->resourceLang === $statement['language']) {
186
            $this->updatedStatements[$statement['property_id']] = $statement['new value'];
187
        }
188
        if ($this->formResourceProps->contains($statement['property_id'])) {
189
            $this->updatedStatements[$statement['property_id']] = $statement['new value'];
190
        }
191
    }
192
193
    private function getStatements(int $reg_id): ?Collection
194
    {
195
        if ($this->isElementSet()) {
196
            $this->resource = Element::find($reg_id)->load('statements');
197
        } else {
198
            $this->resource = Concept::find($reg_id)->load('statements');
199
        }
200
201
        return $this->resource->statements;
202
    }
203
204
    private function setFormproperties(): void
205
    {
206
        $this->formResourceProps =
207
            $this->isElementSet() ? $this->import->elementset->profile->profile_properties()->where([
0 ignored issues
show
Bug introduced by
The method profile_properties() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

207
            $this->isElementSet() ? $this->import->elementset->profile->/** @scrutinizer ignore-call */ profile_properties()->where([

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
208
                ['is_in_form', true],
209
                ['has_language', false],
210
            ])->get(['id'])->pluck('id') : $this->import->vocabulary->profile->profile_properties()->where([
0 ignored issues
show
Bug introduced by
The method profile_properties() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
            ])->get(['id'])->pluck('id') : $this->import->vocabulary->profile->/** @scrutinizer ignore-call */ profile_properties()->where([

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
211
                ['is_in_form', true],
212
                ['has_language', false],
213
            ])->get(['id'])->pluck('id');
214
        $this->formLanguageProps =
215
            $this->isElementSet() ? $this->import->elementset->profile->profile_properties()->where([
216
                ['is_in_form', true],
217
                ['has_language', true],
218
            ])->get(['id'])->pluck('id') : $this->import->vocabulary->profile->profile_properties()->where([
219
                ['is_in_form', true],
220
                ['has_language', true],
221
            ])->get(['id'])->pluck('id');
222
    }
223
224
    private function setLanguage(): void
225
    {
226
        $this->resourceLang =
227
            $this->isElementSet() ? $this->import->elementset->getOriginal('language') : $this->import->vocabulary->getOriginal('language');
0 ignored issues
show
Bug introduced by
The method getOriginal() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

227
            $this->isElementSet() ? $this->import->elementset->getOriginal('language') : $this->import->vocabulary->/** @scrutinizer ignore-call */ getOriginal('language');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
228
    }
229
230
    private function makeResource(int $vocabId): ?Model
231
    {
232
        if ($this->isElementSet()) {
233
            $this->resource = Element::make(['schema_id' => $vocabId]);
234
        } else {
235
            $this->resource = Concept::make(['vocabulary_id' => $vocabId]);
236
        }
237
238
        return $this->resource;
239
    }
240
241
    private function makeStatement(array $statement)
242
    {
243
        $values = [
244
            'object'              => $statement['new value'],
245
            'language'            => $statement['language'],
246
            'profile_property_id' => $statement['property_id'],
247
            'last_import_id'      => $this->import->id,
248
        ];
249
        if ($this->isElementSet()) {
250
            $values['status_id'] = $this->import->elementset->status_id;
251
252
            return ElementAttribute::make($values);
253
        }
254
255
        $values['status_id'] = $this->import->vocabulary->status_id;
256
257
        return ConceptAttribute::make($values);
258
    }
259
260
    private function UpdatePrefLabelId(Model $resource)
261
    {
262
        if (! $this->isElementSet()) {
263
            $id = $resource->statements()
264
                ->where([
265
                    ['profile_property_id', 45],
266
                    ['language', $this->resourceLang],
267
                ])->pluck('id');
268
            $resource->pref_label_id = $id[0] ?? null;
269
            $resource->save();
270
        }
271
    }
272
273
    private function isElementSet(): bool
274
    {
275
        return (bool) $this->import->schema_id;
276
    }
277
278
    private function setResults($element, $value)
279
    {
280
        $this->results[$element][] = $value;
281
    }
282
283
    private function makeErrorLogEntry($rowId, $message)
284
    {
285
        $this->setResults('errors', ['row'=> $rowId, 'message' =>$message]);
286
    }
287
}
288