Passed
Push — master ( 5e2b09...a39721 )
by Ben
09:28 queued 02:28
created

AbstractManager::requestContainsTranslations()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Thinktomorrow\Chief\Management;
6
7
use Illuminate\Support\Facades\Schema;
8
use Illuminate\Contracts\Pagination\Paginator;
9
use Illuminate\Database\Eloquent\Builder;
10
use Illuminate\Http\Request;
11
use Thinktomorrow\Chief\Concerns\Translatable\TranslatableCommand;
12
use Thinktomorrow\Chief\Fields\Fields;
13
use Thinktomorrow\Chief\Fields\SavingFields;
14
use Thinktomorrow\Chief\Fields\Types\Field;
15
use Thinktomorrow\Chief\Filters\Filters;
16
use Thinktomorrow\Chief\Management\Assistants\ManagesAssistants;
17
use Thinktomorrow\Chief\Management\Details\HasDetails;
18
use Thinktomorrow\Chief\Management\Details\HasDetailSections;
19
use Thinktomorrow\Chief\Management\Exceptions\NonExistingRecord;
20
use Thinktomorrow\Chief\Management\Exceptions\NotAllowedManagerRoute;
21
use Thinktomorrow\Chief\Relations\ActsAsChild;
22
use Thinktomorrow\Chief\Relations\ActsAsParent;
23
24
abstract class AbstractManager
25
{
26
    use SavingFields;
27
    use HasDetails;
0 ignored issues
show
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...
28
    use HasDetailSections;
29
    use ManagesMedia;
30
    use ManagesPagebuilder;
0 ignored issues
show
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...
31
    use TranslatableCommand;
32
    use ManagesAssistants;
33
34
    protected $translation_columns = [];
35
36
    protected $model;
37
38
    /** @var Register */
39
    protected $registration;
40
41
    protected $pageCount = 20;
42
    protected $paginated = true;
43
    protected static $bootedTraitMethods = [];
44 169
45
    final public function __construct(Registration $registration)
46 169
    {
47
        $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...
48
49 169
        // Upon instantiation, a general model is set that doesn't point to a persisted record.
50
        $this->manage(app($this->registration->model()));
51
52 169
        // Check if key and model are present since the model should be set by the manager itself
53
        $this->validateConstraints();
54 169
55 169
        static::bootTraitMethods();
56
    }
57 169
58
    public function managerKey(): string
59 169
    {
60
        return $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

60
        return $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...
61 169
    }
62
63
    public function manage($model): Manager
64 98
    {
65
        $this->model = $model;
66 98
67
        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...
68 98
    }
69
70 98
    public function findManaged($id): Manager
71
    {
72
        $modelInstance = $this->modelInstance()::where('id', $id)->withoutGlobalScopes()->first();
0 ignored issues
show
Bug introduced by
The method where() does not exist on Thinktomorrow\Chief\Management\ManagedModel. Since it exists in all sub-types, consider adding an abstract or default implementation to Thinktomorrow\Chief\Management\ManagedModel. ( Ignorable by Annotation )

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

72
        $modelInstance = $this->modelInstance()::/** @scrutinizer ignore-call */ where('id', $id)->withoutGlobalScopes()->first();
Loading history...
73 6
74
        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

74
        return (new static(/** @scrutinizer ignore-type */ $this->registration))->manage($modelInstance);
Loading history...
75 6
    }
76
77 6
    public function indexCollection()
78
    {
79 6
        $builder = ($this->modelInstance())->query();
0 ignored issues
show
Bug introduced by
The method query() does not exist on Thinktomorrow\Chief\Management\ManagedModel. Since it exists in all sub-types, consider adding an abstract or default implementation to Thinktomorrow\Chief\Management\ManagedModel. ( Ignorable by Annotation )

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

79
        $builder = ($this->modelInstance())->/** @scrutinizer ignore-call */ query();
Loading history...
80
81 6
        $this->filters()->apply($builder);
82
83 6
        $builder = $this->indexBuilder($builder);
84
85 6
        $builder = $this->indexSorting($builder);
86 6
87
        if ($this->paginated) {
88
            return $this->indexPagination($builder);
89
        }
90
91
        return $builder->get()->map(function ($model) {
92
            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

92
            return (new static(/** @scrutinizer ignore-type */ $this->registration))->manage($model);
Loading history...
93
        });
94 6
    }
95
96 6
    protected function indexBuilder(Builder $builder): Builder
97
    {
98
        return $builder;
99 6
    }
100
101 6
    protected function indexSorting(Builder $builder): Builder
102 3
    {
103
        if ($this->isAssistedBy('publish') && Schema::hasColumn($this->modelInstance()->getTable(), 'published_at')) {
0 ignored issues
show
Bug introduced by
The method getTable() does not exist on Thinktomorrow\Chief\Management\ManagedModel. Since it exists in all sub-types, consider adding an abstract or default implementation to Thinktomorrow\Chief\Management\ManagedModel. ( Ignorable by Annotation )

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

103
        if ($this->isAssistedBy('publish') && Schema::hasColumn($this->modelInstance()->/** @scrutinizer ignore-call */ getTable(), 'published_at')) {
Loading history...
104
            $builder->orderBy('published_at', 'DESC');
105
        }
106 6
107 6
        // if model has no timestamps, updated_at doesn't exist
108
        if ($this->modelInstance()->timestamps) {
0 ignored issues
show
Bug introduced by
Accessing timestamps on the interface Thinktomorrow\Chief\Management\ManagedModel suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
109
            $builder->orderBy('updated_at', 'DESC');
110 6
        }
111
112
        return $builder;
113 6
    }
114
115 6
    protected function indexPagination($builder): Paginator
116
    {
117
        $paginator = $builder->paginate($this->pageCount);
118 4
119 6
        $modifiedCollection = $builder->paginate($this->pageCount)->getCollection()->transform(function ($model) {
120
            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

120
            return (new static(/** @scrutinizer ignore-type */ $this->registration))->manage($model);
Loading history...
121 6
        });
122
123
        return $paginator->setCollection($modifiedCollection);
124 106
    }
125
126 106
    public function modelInstance(): ManagedModel
127
    {
128
        $class = $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

128
        /** @scrutinizer ignore-call */ 
129
        $class = $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...
129 119
130
        return new $class();
131 119
    }
132
133
    public function existingModel(): ManagedModel
134
    {
135
        if (!$this->hasExistingModel()) {
136
            throw new NonExistingRecord('Model does not exist yet but is expected.');
137
        }
138
139
        return $this->model;
140 119
    }
141
142 119
    public function hasExistingModel(): bool
143 1
    {
144
        return ($this->model && $this->model->exists);
145
    }
146 118
147
    /**
148
     * Determine which actions should be available for this
149
     * manager and their respective routed urls.
150
     *
151
     * @param $verb
152
     * @return null|string
153
     * @throws NonExistingRecord
154
     */
155
    public function route($verb): ?string
156
    {
157 130
        $routes = [
158
            'index'  => route('chief.back.managers.index', [$this->registration->key()]),
159
            'create' => route('chief.back.managers.create', [$this->registration->key()]),
160 130
            'store'  => route('chief.back.managers.store', [$this->registration->key()]),
161 130
        ];
162 130
163
        if (array_key_exists($verb, $routes)) {
164
            return $routes[$verb] ?? null;
165 130
        }
166 76
167
        //These routes expect the model to be persisted in the database
168
        $modelRoutes = [
169
            'edit'   => route('chief.back.managers.edit', [$this->registration->key(), $this->existingModel()->id]),
0 ignored issues
show
Bug introduced by
Accessing id on the interface Thinktomorrow\Chief\Management\ManagedModel suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
170
            'update' => route('chief.back.managers.update', [$this->registration->key(), $this->existingModel()->id]),
171 107
            'delete' => route('chief.back.managers.delete', [$this->registration->key(), $this->existingModel()->id]),
172 106
            'upload' => route('chief.back.managers.media.upload', [
173 106
                $this->registration->key(),
174 106
                $this->existingModel()->id,
175
            ]),
176
        ];
177 106
178
        return $modelRoutes[$verb] ?? null;
179
    }
180 140
181
    public function can($verb): bool
182 140
    {
183 8
        foreach (static::$bootedTraitMethods['can'] as $method) {
184
            if (!method_exists($this, $method)) {
185
                continue;
186 8
            }
187
            $this->$method($verb);
188
        }
189 136
190
        return !is_null($this->route($verb));
191
    }
192 126
193
    public function guard($verb): Manager
194 126
    {
195 5
        if (!$this->can($verb)) {
196
            NotAllowedManagerRoute::notAllowedVerb($verb, $this);
197
        }
198 121
199
        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...
200
    }
201 87
202
    public function fields(): Fields
203 87
    {
204
        return new Fields();
205
    }
206
207
    /**
208
     * Enrich the manager fields with any of the assistant specified fields
209
     *
210
     * @return Fields
211
     * @throws \Exception
212 111
     */
213
    public function fieldsWithAssistantFields(): Fields
214 111
    {
215
        $fields = $this->fields();
216 111
217 85
        foreach ($this->assistantsAsClassNames() as $assistantClass) {
218 83
            if (!method_exists($assistantClass, 'fields')) {
219
                continue;
220
            }
221 85
222
            $fields = $fields->merge($this->assistant($assistantClass)->fields());
0 ignored issues
show
Bug introduced by
The method fields() does not exist on Thinktomorrow\Chief\Mana...nt\Assistants\Assistant. It seems like you code against a sub-type of Thinktomorrow\Chief\Mana...nt\Assistants\Assistant such as Thinktomorrow\Chief\Mana...Assistants\UrlAssistant or Thinktomorrow\Chief\Test...FakeAssistantWithFields. ( Ignorable by Annotation )

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

222
            $fields = $fields->merge($this->assistant($assistantClass)->/** @scrutinizer ignore-call */ fields());
Loading history...
Bug introduced by
$assistantClass of type object is incompatible with the type string expected by parameter $assistantKey of Thinktomorrow\Chief\Mana...actManager::assistant(). ( Ignorable by Annotation )

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

222
            $fields = $fields->merge($this->assistant(/** @scrutinizer ignore-type */ $assistantClass)->fields());
Loading history...
223
        }
224 111
225
        return $fields;
226
    }
227
228
    public function createFields(): Fields
229
    {
230
        return $this->fieldsWithAssistantFields();
231
    }
232
233
    public function editFields(): Fields
234
    {
235
        return $this->fieldsWithAssistantFields()->map(function(Field $field){
236 3
            return $field->model($this->model);
0 ignored issues
show
Bug introduced by
The method model() does not exist on Thinktomorrow\Chief\Fields\Types\Field. Since it exists in all sub-types, consider adding an abstract or default implementation to Thinktomorrow\Chief\Fields\Types\Field. ( Ignorable by Annotation )

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

236
            return $field->/** @scrutinizer ignore-call */ model($this->model);
Loading history...
237
        });
238 3
    }
239
240
    public function createView(): string
241 2
    {
242
        return 'chief::back.managers._partials._form';
243 2
    }
244 2
245
    public function createViewData(): array
246
    {
247 2
        return [];
248 2
    }
249
250
    public function editView(): string
251 2
    {
252 2
        return 'chief::back.managers._partials._form';
253
    }
254 3
255
    public function editViewData(): array
256 3
    {
257
        return [];
258
    }
259
260
    public function delete()
261
    {
262
        $this->guard('delete');
263
264
        if ($this->model instanceof ActsAsChild) {
265
            $this->model->detachAllParentRelations();
266 26
        }
267
268 26
        if ($this->model instanceof ActsAsParent) {
269
            $this->model->detachAllChildRelations();
270
        }
271
272
        $this->model->delete();
273
    }
274
275
    public static function filters(): Filters
276
    {
277
        return new Filters();
278 24
    }
279
280 24
    /**
281
     * This method can be used to manipulate the store request payload
282
     * before being passed to the storing / updating the models.
283 83
     *
284
     * @param Request $request
285 83
     * @return Request
286
     */
287
    public function storeRequest(Request $request): Request
288 169
    {
289
        return $request;
290 169
    }
291
292
    /**
293 169
     * This method can be used to manipulate the update request payload
294
     * before being passed to the storing / updating the models.
295 169
     *
296
     * @param Request $request
297 169
     * @return Request
298
     */
299
    public function updateRequest(Request $request): Request
300 169
    {
301
        return $request;
302
    }
303 169
304 169
    protected function requestContainsTranslations(Request $request): bool
305
    {
306 169
        return $request->has('trans');
307 169
    }
308
309 169
    protected function validateConstraints()
310 169
    {
311
        if (!$this->model) {
312
            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.');
313
        }
314 169
    }
315
316
    public static function bootTraitMethods()
317
    {
318
        $class = static::class;
319
320
        $methods = [
321
            'can',
322
        ];
323
324
        foreach ($methods as $baseMethod) {
325
            static::$bootedTraitMethods[$baseMethod] = [];
326
327
            foreach (class_uses_recursive($class) as $trait) {
328
                $method = class_basename($trait) . ucfirst($baseMethod);
329
330
                if (method_exists($class, $method) && !in_array($method, static::$bootedTraitMethods[$baseMethod])) {
331
                    static::$bootedTraitMethods[$baseMethod][] = lcfirst($method);
332
                }
333
            }
334
        }
335
    }
336
}
337