Completed
Push — 0.3 ( da43ab...625b56 )
by Ben
96:17 queued 52:29
created

AbstractManager::hasExistingModel()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
namespace Thinktomorrow\Chief\Management;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Support\Collection;
7
use Thinktomorrow\Chief\Concerns\Translatable\TranslatableCommand;
8
use Thinktomorrow\Chief\Fields\FieldArrangement;
9
use Thinktomorrow\Chief\Fields\Fields;
10
use Thinktomorrow\Chief\Fields\Types\Field;
11
use Thinktomorrow\Chief\Fields\Types\FieldType;
12
use Thinktomorrow\Chief\Filters\Filters;
13
use Thinktomorrow\Chief\Management\Assistants\AssistedManager;
14
use Thinktomorrow\Chief\Management\Details\HasDetails;
15
use Thinktomorrow\Chief\Management\Details\HasSections;
16
use Thinktomorrow\Chief\Management\Exceptions\NonExistingRecord;
17
use Thinktomorrow\Chief\Management\Exceptions\NotAllowedManagerRoute;
18
19
abstract class AbstractManager
20
{
21
    use HasDetails,
0 ignored issues
show
Bug introduced by
The trait Thinktomorrow\Chief\Management\ManagesMedia requires the property $id which is not provided by Thinktomorrow\Chief\Management\AbstractManager.
Loading history...
introduced by
The trait Thinktomorrow\Chief\Management\Details\HasDetails requires some properties which are not provided by Thinktomorrow\Chief\Management\AbstractManager: $labelPlural, $id, $labelSingular, $title
Loading history...
introduced by
The trait Thinktomorrow\Chief\Management\ManagesPagebuilder requires some properties which are not provided by Thinktomorrow\Chief\Management\AbstractManager: $trans, $slug, $page_id, $id
Loading history...
22
        HasSections,
23
        ManagesMedia,
24
        ManagesPagebuilder,
25
        TranslatableCommand,
26
        AssistedManager;
27
28
    protected $queued_translations = [];
29
    protected $translation_columns = [];
30
31
    protected $model;
32
33
    /** @var Register */
34
    protected $registration;
35
36
    /** @var string */
37
    protected $key;
38
39 129
    public function __construct(Registration $registration)
40
    {
41 129
        $this->registration = $registration;
0 ignored issues
show
Documentation Bug introduced by
It seems like $registration of type Thinktomorrow\Chief\Management\Registration is incompatible with the declared type Thinktomorrow\Chief\Management\Register of property $registration.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
42
43
        // Upon instantiation, a general model is set that doesn't point to a persisted record.
44 129
        $this->manage(app($this->registration->model()));
45
46
        // Check if key and model are present since the model should be set by the manager itself
47 129
        $this->validateConstraints();
48 129
    }
49
50 129
    public function manage($model): Manager
51
    {
52 129
        $this->model = $model;
53
54 129
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Thinktomorrow\Chief\Management\AbstractManager which is incompatible with the type-hinted return Thinktomorrow\Chief\Management\Manager.
Loading history...
55
    }
56
57 77
    public function findManaged($id): Manager
58
    {
59 77
        $model = $this->registration->model();
0 ignored issues
show
Bug introduced by
The method model() does not exist on Thinktomorrow\Chief\Management\Register. ( Ignorable by Annotation )

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

59
        /** @scrutinizer ignore-call */ 
60
        $model = $this->registration->model();

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...
60
61 77
        $modelInstance = $model::where('id', $id)->withoutGlobalScopes()->first();
62
63 77
        return (new static($this->registration))->manage($modelInstance);
0 ignored issues
show
Bug introduced by
$this->registration of type Thinktomorrow\Chief\Management\Register is incompatible with the type Thinktomorrow\Chief\Management\Registration expected by parameter $registration of Thinktomorrow\Chief\Mana...tManager::__construct(). ( Ignorable by Annotation )

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

63
        return (new static(/** @scrutinizer ignore-type */ $this->registration))->manage($modelInstance);
Loading history...
64
    }
65
66
    public function findAllManaged($apply_filters = false): Collection
67
    {
68
        $model = $this->registration->model();
69
70
        if ($apply_filters) {
71
            $builder = (new $model)->query();
72
            $this->filters()->apply($builder);
73
74
            $results = $builder->get();
75
        } else {
76
            $results = $model::all();
77
        }
78
79
        return $results->map(function ($model) {
80
            return (new static($this->registration))->manage($model);
0 ignored issues
show
Bug introduced by
$this->registration of type Thinktomorrow\Chief\Management\Register is incompatible with the type Thinktomorrow\Chief\Management\Registration expected by parameter $registration of Thinktomorrow\Chief\Mana...tManager::__construct(). ( Ignorable by Annotation )

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

80
            return (new static(/** @scrutinizer ignore-type */ $this->registration))->manage($model);
Loading history...
81
        });
82
    }
83
84 75
    public function model()
85
    {
86 75
        return $this->model;
87
    }
88
89 84
    public function hasExistingModel(): bool
90
    {
91 84
        return ($this->model && $this->model->exists);
92
    }
93
94
    /**
95
     * If the model exists return it otherwise
96
     * throws a nonExistingRecord exception;
97
     *
98
     * @throws NonExistingRecord
99
     */
100 84
    protected function existingModel()
101
    {
102 84
        if (!$this->hasExistingModel()) {
103 1
            throw new NonExistingRecord('Model does not exist yet but is expected.');
104
        }
105
106 83
        return $this->model;
107
    }
108
109
    /**
110
     * Determine which actions should be available for this
111
     * manager and their respective routed urls.
112
     *
113
     * @param $verb
114
     * @return null|string
115
     * @throws NonExistingRecord
116
     */
117 102
    public function route($verb): ?string
118
    {
119
        $routes = [
120 102
            'index'   => route('chief.back.managers.index', [$this->registration->key()]),
0 ignored issues
show
Bug introduced by
The method key() does not exist on Thinktomorrow\Chief\Management\Register. ( Ignorable by Annotation )

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

120
            'index'   => route('chief.back.managers.index', [$this->registration->/** @scrutinizer ignore-call */ key()]),

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...
121 102
            'create'  => route('chief.back.managers.create', [$this->registration->key()]),
122 102
            'store'   => route('chief.back.managers.store', [$this->registration->key()]),
123
        ];
124
125 102
        if (array_key_exists($verb, $routes)) {
126 56
            return $routes[$verb] ?? null;
127
        }
128
129
        //These routes expect the model to be persisted in the database
130
        $modelRoutes = [
131 84
            'edit'    => route('chief.back.managers.edit', [$this->registration->key(), $this->existingModel()->id]),
132 83
            'update'  => route('chief.back.managers.update', [$this->registration->key(), $this->existingModel()->id]),
133 83
            'delete'  => route('chief.back.managers.delete', [$this->registration->key(), $this->existingModel()->id]),
134 83
            'upload'  => route('chief.back.managers.media.upload', [$this->registration->key(), $this->existingModel()->id]),
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 125 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
135
        ];
136
137 83
        return $modelRoutes[$verb] ?? null;
138
    }
139
140 105
    public function can($verb): bool
141
    {
142 105
        return !is_null($this->route($verb));
143
    }
144
145 95
    public function guard($verb): Manager
146
    {
147 95
        if (! $this->can($verb)) {
148 1
            NotAllowedManagerRoute::notAllowedVerb($verb, $this);
149
        }
150
151 94
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Thinktomorrow\Chief\Management\AbstractManager which is incompatible with the type-hinted return Thinktomorrow\Chief\Management\Manager.
Loading history...
152
    }
153
154 74
    public function fields(): Fields
155
    {
156 74
        return new Fields();
157
    }
158
159
    /**
160
     * Enrich the manager fields with any of the assistant specified fields
161
     *
162
     * @return Fields
163
     * @throws \Exception
164
     */
165 91
    public function fieldsWithAssistantFields(): Fields
166
    {
167 91
        $fields = $this->fields();
168
169 91
        foreach ($this->assistants() as $assistant) {
170 67
            if (! method_exists($assistant, 'fields')) {
171 65
                continue;
172
            }
173
174 67
            $fields = $fields->merge($assistant->fields());
175
        }
176
177 91
        return $fields;
178
    }
179
180
    /**
181
     * This determines the arrangement of the manageable fields
182
     * on the create and edit forms. By default, all fields
183
     * are presented in their order of appearance
184
     *
185
     * @param null $key pinpoint to a specific field arrangement e.g. for create page.
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $key is correct as it would always require null to be passed?
Loading history...
186
     * @return FieldArrangement
187
     * @throws \Exception
188
     */
189 3
    public function fieldArrangement($key = null): FieldArrangement
190
    {
191 3
        return new FieldArrangement($this->fieldsWithAssistantFields());
192
    }
193
194 5
    public function getFieldValue($field, $default = null)
195
    {
196
        // If string is passed, we use this to find the proper field
197 5
        if (is_string($field)) {
198 5
            foreach ($this->fieldsWithAssistantFields()->all() as $possibleField) {
199 5
                if ($possibleField->key() == $field) {
200 3
                    $field = $possibleField;
201 5
                    break;
202
                }
203
            }
204
205 5
            if (is_string($field)) {
206
207
                // Could be translatable field
208 4
                if ($this->isTranslatableKey($field)) {
209 4
                    $attribute = substr($field, strrpos($field, '.') + 1);
210 4
                    $locale = substr($field, strlen('trans.'), 2);
211
212 4
                    return $this->model->getTranslationFor($attribute, $locale);
0 ignored issues
show
Bug introduced by
The method getTranslationFor() does not exist on Illuminate\Contracts\Foundation\Application. ( Ignorable by Annotation )

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

212
                    return $this->model->/** @scrutinizer ignore-call */ getTranslationFor($attribute, $locale);

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...
213
                }
214
215
                return $default;
216
            }
217
        }
218
219
        // Is it a media field
220
        // An array grouped by type is returned. Each media array has an id, filename and path.
221 4
        if ($field->ofType(FieldType::MEDIA)) {
222 3
            return $this->populateMedia($this->model);
0 ignored issues
show
Bug introduced by
It seems like $this->model can also be of type Illuminate\Contracts\Foundation\Application; however, parameter $model of Thinktomorrow\Chief\Mana...anager::populateMedia() does only seem to accept Spatie\MediaLibrary\HasMedia\HasMedia, maybe add an additional type check? ( Ignorable by Annotation )

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

222
            return $this->populateMedia(/** @scrutinizer ignore-type */ $this->model);
Loading history...
223
        }
224
225 2
        if ($field->ofType(FieldType::DOCUMENT)) {
226 1
            return $this->populateDocuments($this->model);
0 ignored issues
show
Bug introduced by
It seems like $this->model can also be of type Illuminate\Contracts\Foundation\Application; however, parameter $model of Thinktomorrow\Chief\Mana...er::populateDocuments() does only seem to accept Spatie\MediaLibrary\HasMedia\HasMedia, maybe add an additional type check? ( Ignorable by Annotation )

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

226
            return $this->populateDocuments(/** @scrutinizer ignore-type */ $this->model);
Loading history...
227
        }
228
229 2
        return $this->model->{$field->column()};
230
    }
231
232 4
    private function isTranslatableKey(string $key): bool
233
    {
234 4
        return 0 === strpos($key, 'trans.');
235
    }
236
237 70
    public function setField(Field $field, Request $request)
238
    {
239
        // Is field set as translatable?
240 70
        if ($field->isTranslatable()) {
241 68
            if (!$this->requestContainsTranslations($request)) {
242 24
                return;
243
            }
244
245
            // Make our media fields able to be translatable as well...
246 45
            if ($field->ofType(FieldType::MEDIA, FieldType::DOCUMENT)) {
247
                throw new \Exception('Cannot process the ' . $field->key . ' media field. Currently no support for translatable media files. We should fix this!');
0 ignored issues
show
Bug Best Practice introduced by
The property key does not exist on Thinktomorrow\Chief\Fields\Types\Field. Since you implemented __get, consider adding a @property annotation.
Loading history...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 163 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
248
            }
249
250
            // Okay so this is a bit odd but since all translations are expected to be inside the trans
251
            // array, we can add all these translations at once. Just make sure to keep track of the
252
            // keys since this is what our translation engine requires as well for proper update.
253 45
            $this->queued_translations = $request->get('trans');
254 45
            $this->translation_columns[] = $field->column();
0 ignored issues
show
Bug introduced by
The method column() does not exist on Thinktomorrow\Chief\Fields\Types\Field. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

254
            /** @scrutinizer ignore-call */ 
255
            $this->translation_columns[] = $field->column();
Loading history...
255
256 45
            return;
257
        }
258
259
        // By default we assume the key matches the attribute / column naming
260 35
        $this->model->{$field->column()} = $request->get($field->key());
0 ignored issues
show
Bug introduced by
The method key() does not exist on Thinktomorrow\Chief\Fields\Types\Field. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

260
        $this->model->{$field->column()} = $request->get($field->/** @scrutinizer ignore-call */ key());
Loading history...
261 35
    }
262
263 76
    public function saveFields(): Manager
264
    {
265 76
        $this->model->save();
0 ignored issues
show
Bug introduced by
The method save() does not exist on Illuminate\Contracts\Foundation\Application. ( Ignorable by Annotation )

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

265
        $this->model->/** @scrutinizer ignore-call */ 
266
                      save();

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...
266
267
        // Translations
268 76
        if (!empty($this->queued_translations)) {
269 43
            $this->saveTranslations($this->queued_translations, $this->model, $this->translation_columns);
0 ignored issues
show
Bug introduced by
It seems like $this->model can also be of type Illuminate\Contracts\Foundation\Application; however, parameter $entity of Thinktomorrow\Chief\Mana...ger::saveTranslations() does only seem to accept Thinktomorrow\Chief\Conc...le\TranslatableContract, maybe add an additional type check? ( Ignorable by Annotation )

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

269
            $this->saveTranslations($this->queued_translations, /** @scrutinizer ignore-type */ $this->model, $this->translation_columns);
Loading history...
270
        }
271
272 76
        return (new static($this->registration))->manage($this->model);
0 ignored issues
show
Bug introduced by
$this->registration of type Thinktomorrow\Chief\Management\Register is incompatible with the type Thinktomorrow\Chief\Management\Registration expected by parameter $registration of Thinktomorrow\Chief\Mana...tManager::__construct(). ( Ignorable by Annotation )

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

272
        return (new static(/** @scrutinizer ignore-type */ $this->registration))->manage($this->model);
Loading history...
273
    }
274
275
276
277 1
    public function delete()
278
    {
279 1
        $this->model->delete();
0 ignored issues
show
Bug introduced by
The method delete() does not exist on Illuminate\Contracts\Foundation\Application. ( Ignorable by Annotation )

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

279
        $this->model->/** @scrutinizer ignore-call */ 
280
                      delete();

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...
280 1
    }
281
282 5
    public function renderField(Field $field)
283
    {
284 5
        return view($field->view(), array_merge([
0 ignored issues
show
Bug introduced by
It seems like $field->view() can also be of type Thinktomorrow\Chief\Fields\Types\Field; however, parameter $view of view() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

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

284
        return view(/** @scrutinizer ignore-type */ $field->view(), array_merge([
Loading history...
285 5
            'field'           => $field,
286 5
            'key'             => $field->key, // As parameter so that it can be altered for translatable values
0 ignored issues
show
Bug Best Practice introduced by
The property key does not exist on Thinktomorrow\Chief\Fields\Types\Field. Since you implemented __get, consider adding a @property annotation.
Loading history...
287 5
            'manager'         => $this,
288 5
            'formElementView' => $field->formElementView(),
289 5
        ]), $field->viewData())->render();
0 ignored issues
show
Bug introduced by
It seems like $field->viewData() can also be of type Thinktomorrow\Chief\Fields\Types\Field; however, parameter $mergeData of view() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

289
        ]), /** @scrutinizer ignore-type */ $field->viewData())->render();
Loading history...
290
    }
291
292
    public static function filters(): Filters
293
    {
294
        return new Filters();
295
    }
296
297
    /**
298
     * This method can be used to manipulate the store request payload
299
     * before being passed to the storing / updating the models.
300
     *
301
     * @param Request $request
302
     * @return Request
303
     */
304 19
    public function storeRequest(Request $request): Request
305
    {
306 19
        return $request;
307
    }
308
309
    /**
310
     * This method can be used to manipulate the update request payload
311
     * before being passed to the storing / updating the models.
312
     *
313
     * @param Request $request
314
     * @return Request
315
     */
316 22
    public function updateRequest(Request $request): Request
317
    {
318 22
        return $request;
319
    }
320
321 68
    protected function requestContainsTranslations(Request $request): bool
322
    {
323 68
        return $request->has('trans');
324
    }
325
326 129
    protected function validateConstraints()
327
    {
328 129
        if (!$this->model) {
329
            throw new \DomainException('Model class should be set for this manager. Please set the model property default via the constructor or by extending the setupDefaults method.');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 186 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
330
        }
331 129
    }
332
}
333