Passed
Push — analysis-z4nkaD ( 2af593 )
by Philippe
11:48 queued 27s
created

AbstractManager::bootTraitMethod()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 11
rs 10
cc 4
nc 3
nop 1
1
<?php
2
3
namespace Thinktomorrow\Chief\Management;
4
5
use Illuminate\Contracts\Pagination\Paginator;
6
use Illuminate\Database\Eloquent\Builder;
7
use Illuminate\Http\Request;
8
use Thinktomorrow\Chief\Concerns\Translatable\TranslatableCommand;
9
use Thinktomorrow\Chief\Fields\FieldArrangement;
10
use Thinktomorrow\Chief\Fields\Fields;
11
use Thinktomorrow\Chief\Fields\RenderingFields;
12
use Thinktomorrow\Chief\Fields\SavingFields;
13
use Thinktomorrow\Chief\Filters\Filters;
14
use Thinktomorrow\Chief\Management\Assistants\AssistedManager;
15
use Thinktomorrow\Chief\Management\Details\HasDetails;
16
use Thinktomorrow\Chief\Management\Details\HasSections;
17
use Thinktomorrow\Chief\Management\Exceptions\NonExistingRecord;
18
use Thinktomorrow\Chief\Management\Exceptions\NotAllowedManagerRoute;
19
20
abstract class AbstractManager
21
{
22
    use RenderingFields,
0 ignored issues
show
Bug introduced by
The trait Thinktomorrow\Chief\Fields\SavingFields requires the property $key which is not provided by Thinktomorrow\Chief\Management\AbstractManager.
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...
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...
23
        SavingFields,
24
        HasDetails,
25
        HasSections,
26
        ManagesMedia,
27
        ManagesPagebuilder,
28
        TranslatableCommand,
29
        AssistedManager;
30
31
    protected $translation_columns = [];
32
33
    protected $model;
34
35
    /** @var Register */
36
    protected $registration;
37
38
    protected $pageCount = 20;
39
    protected $paginated = true;
40
41
    public function __construct(Registration $registration)
42
    {
43
        $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...
44
45
        // Upon instantiation, a general model is set that doesn't point to a persisted record.
46
        $this->manage(app($this->registration->model()));
47
48
        // Check if key and model are present since the model should be set by the manager itself
49
        $this->validateConstraints();
50
51
        $this->bootTraitMethod('can');
52
    }
53
54
    public function manage($model): Manager
55
    {
56
        $this->model = $model;
57
58
        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...
59
    }
60
61
    public function findManaged($id): Manager
62
    {
63
        $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

63
        /** @scrutinizer ignore-call */ 
64
        $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...
64
65
        $modelInstance = $model::where('id', $id)->withoutGlobalScopes()->first();
66
67
        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

67
        return (new static(/** @scrutinizer ignore-type */ $this->registration))->manage($modelInstance);
Loading history...
68
    }
69
70
    public function indexCollection()
71
    {
72
        $model = $this->registration->model();
73
74
        $builder = (new $model)->query();
75
76
        $this->filters()->apply($builder);
77
78
        $builder = $this->indexBuilder($builder);
79
80
        $builder = $this->indexSorting($builder);
81
        
82
        if ($this->paginated) {
83
            return $this->indexPagination($builder);
84
        }
85
        
86
        return $builder->get()->map(function ($model) {
87
            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

87
            return (new static(/** @scrutinizer ignore-type */ $this->registration))->manage($model);
Loading history...
88
        });
89
    }
90
91
    protected function indexBuilder(Builder $builder): Builder
92
    {
93
        return $builder;
94
    }
95
96
    protected function indexSorting(Builder $builder): Builder
97
    {
98
        if ($this->isAssistedBy('publish')) {
99
            $builder->orderBy('published', 'DESC');
100
        }
101
102
        // if model has no timestamps, updated_at doesn't exist
103
        if ($this->model()->timestamps) {
104
            $builder->orderBy('updated_at', 'DESC');
105
        }
106
107
        return $builder;
108
    }
109
110
    protected function indexPagination($builder): Paginator
111
    {
112
        $paginator = $builder->paginate($this->pageCount);
113
114
        $modifiedCollection = $builder->paginate($this->pageCount)->getCollection()->transform(function ($model) {
115
            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

115
            return (new static(/** @scrutinizer ignore-type */ $this->registration))->manage($model);
Loading history...
116
        });
117
118
        return $paginator->setCollection($modifiedCollection);
119
    }
120
121
    public function model()
122
    {
123
        return $this->model;
124
    }
125
126
    public function hasExistingModel(): bool
127
    {
128
        return ($this->model && $this->model->exists);
129
    }
130
131
    /**
132
     * If the model exists return it otherwise
133
     * throws a nonExistingRecord exception;
134
     *
135
     * @throws NonExistingRecord
136
     */
137
    protected function existingModel()
138
    {
139
        if (!$this->hasExistingModel()) {
140
            throw new NonExistingRecord('Model does not exist yet but is expected.');
141
        }
142
143
        return $this->model;
144
    }
145
146
    /**
147
     * Determine which actions should be available for this
148
     * manager and their respective routed urls.
149
     *
150
     * @param $verb
151
     * @return null|string
152
     * @throws NonExistingRecord
153
     */
154
    public function route($verb): ?string
155
    {
156
        $routes = [
157
            '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

157
            '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...
158
            'create'  => route('chief.back.managers.create', [$this->registration->key()]),
159
            'store'   => route('chief.back.managers.store', [$this->registration->key()]),
160
        ];
161
162
        if (array_key_exists($verb, $routes)) {
163
            return $routes[$verb] ?? null;
164
        }
165
166
        //These routes expect the model to be persisted in the database
167
        $modelRoutes = [
168
            'edit'    => route('chief.back.managers.edit', [$this->registration->key(), $this->existingModel()->id]),
169
            'update'  => route('chief.back.managers.update', [$this->registration->key(), $this->existingModel()->id]),
170
            'delete'  => route('chief.back.managers.delete', [$this->registration->key(), $this->existingModel()->id]),
171
            'upload'  => route('chief.back.managers.media.upload', [$this->registration->key(), $this->existingModel()->id]),
172
        ];
173
174
        return $modelRoutes[$verb] ?? null;
175
    }
176
177
    public function can($verb): bool
178
    {
179
        foreach ($this->bootedcan as $method) {
0 ignored issues
show
Bug Best Practice introduced by
The property bootedcan does not exist on Thinktomorrow\Chief\Management\AbstractManager. Did you maybe forget to declare it?
Loading history...
180
            $this->$method($verb);
181
        }
182
        
183
        return !is_null($this->route($verb));
184
    }
185
186
    public function guard($verb): Manager
187
    {
188
        if (! $this->can($verb)) {
189
            NotAllowedManagerRoute::notAllowedVerb($verb, $this);
190
        }
191
192
        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...
193
    }
194
195
    public function fields(): Fields
196
    {
197
        return new Fields();
198
    }
199
200
    /**
201
     * Enrich the manager fields with any of the assistant specified fields
202
     *
203
     * @return Fields
204
     * @throws \Exception
205
     */
206
    public function fieldsWithAssistantFields(): Fields
207
    {
208
        $fields = $this->fields();
209
210
        foreach ($this->assistants() as $assistant) {
211
            if (! method_exists($assistant, 'fields')) {
212
                continue;
213
            }
214
215
            $fields = $fields->merge($assistant->fields());
216
        }
217
218
        return $fields;
219
    }
220
221
    /**
222
     * This determines the arrangement of the manageable fields
223
     * on the create and edit forms. By default, all fields
224
     * are presented in their order of appearance
225
     *
226
     * @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...
227
     * @return FieldArrangement
228
     * @throws \Exception
229
     */
230
    public function fieldArrangement($key = null): FieldArrangement
231
    {
232
        return new FieldArrangement($this->fieldsWithAssistantFields());
233
    }
234
235
    public function delete()
236
    {
237
        $this->model->delete();
238
    }
239
240
    public static function filters(): Filters
241
    {
242
        return new Filters();
243
    }
244
245
    /**
246
     * This method can be used to manipulate the store request payload
247
     * before being passed to the storing / updating the models.
248
     *
249
     * @param Request $request
250
     * @return Request
251
     */
252
    public function storeRequest(Request $request): Request
253
    {
254
        return $request;
255
    }
256
257
    /**
258
     * This method can be used to manipulate the update request payload
259
     * before being passed to the storing / updating the models.
260
     *
261
     * @param Request $request
262
     * @return Request
263
     */
264
    public function updateRequest(Request $request): Request
265
    {
266
        return $request;
267
    }
268
269
    protected function requestContainsTranslations(Request $request): bool
270
    {
271
        return $request->has('trans');
272
    }
273
274
    protected function validateConstraints()
275
    {
276
        if (!$this->model) {
277
            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.');
278
        }
279
    }
280
281
    public function bootTraitMethod(string $baseMethod)
282
    {
283
        $class = static::class;
284
285
        $this->{'booted'.$baseMethod} = [];
286
        
287
        foreach (class_uses_recursive($class) as $trait) {
288
            $method = class_basename($trait) . ucfirst($baseMethod);
289
            
290
            if (method_exists($class, $method) && ! in_array($method, $this->{'booted'.$baseMethod})) {
291
                $this->{'booted'.$baseMethod}[] = lcfirst($method);
292
            }
293
        }
294
    }
295
}
296