Layout::getResolved()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace NovaFlexibleContent\Layouts;
4
5
use Illuminate\Database\Eloquent\Concerns\HidesAttributes;
6
use Illuminate\Support\Collection;
7
use Laravel\Nova\Fields\Field;
8
use Laravel\Nova\Fields\FieldCollection;
9
use Laravel\Nova\Support\Fluent;
10
use NovaFlexibleContent\Concerns\HasFlexible;
11
use NovaFlexibleContent\Http\ScopedRequest;
12
use NovaFlexibleContent\Layouts\LayoutTraits\Collapsable;
13
use NovaFlexibleContent\Layouts\LayoutTraits\HasFieldsCollection;
14
use NovaFlexibleContent\Layouts\LayoutTraits\HasFieldsRules;
15
use NovaFlexibleContent\Layouts\LayoutTraits\HasFlexibleFieldInLayout;
16
use NovaFlexibleContent\Layouts\LayoutTraits\HasGroupDescription;
17
use NovaFlexibleContent\Layouts\LayoutTraits\HasLayoutKey;
18
use NovaFlexibleContent\Layouts\LayoutTraits\HasLimitPerLayout;
19
use NovaFlexibleContent\Layouts\LayoutTraits\HasModel;
20
use NovaFlexibleContent\Layouts\LayoutTraits\HasNameAndTitle;
21
use NovaFlexibleContent\Layouts\LayoutTraits\HasRemoveCallback;
22
use NovaFlexibleContent\Layouts\LayoutTraits\ModelEmulates;
23
24
/**
25
 * @extends Fluent<string, mixed>
26
 */
27
class Layout extends Fluent
28
{
29
    use HidesAttributes;
30
    use HasFlexible;
31
    use Collapsable;
32
    use ModelEmulates;
0 ignored issues
show
introduced by
The trait NovaFlexibleContent\Layo...outTraits\ModelEmulates requires some properties which are not provided by NovaFlexibleContent\Layouts\Layout: $preventsLazyLoading, $map, $set, $withoutObjectCaching, $withCaching, $withObjectCaching, $get
Loading history...
33
    use HasNameAndTitle;
34
    use HasLayoutKey;
35
    use HasFieldsCollection;
36
    use HasGroupDescription;
37
    use HasLimitPerLayout;
38
    use HasRemoveCallback;
0 ignored issues
show
introduced by
The trait NovaFlexibleContent\Layo...raits\HasRemoveCallback requires some properties which are not provided by NovaFlexibleContent\Layouts\Layout: $attribute, $deleteCallback
Loading history...
39
    use HasFlexibleFieldInLayout;
40
    use HasFieldsRules;
0 ignored issues
show
Bug introduced by
The trait NovaFlexibleContent\Layo...utTraits\HasFieldsRules requires the property $attribute which is not provided by NovaFlexibleContent\Layouts\Layout.
Loading history...
41
    use HasModel;
0 ignored issues
show
Deprecated Code introduced by
The trait NovaFlexibleContent\Layouts\LayoutTraits\HasModel has been deprecated. ( Ignorable by Annotation )

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

41
    use /** @scrutinizer ignore-deprecated */ HasModel;

This trait has been deprecated. The supplier of the trait has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the trait will be removed and what other trait to use instead.

Loading history...
42
43 37
    public function __construct(
44
        ?string               $title = null,
45
        ?string               $name = null,
46
        Collection|array|null $fields = null,
47
        ?string               $key = null,
48
        array                 $attributes = [],
49
        ?\Closure             $removeCallbackMethod = null
50
    ) {
51
        // Override properties or set default provided by developer
52 37
        $this->title                = $title ?? $this->title;
53 37
        $this->name                 = $name  ?? $this->name;
54 37
        $this->fields               = FieldCollection::make($fields);
0 ignored issues
show
Bug introduced by
It seems like $fields can also be of type array; however, parameter $items of Illuminate\Support\Collection::make() does only seem to accept Illuminate\Contracts\Support\Arrayable, 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

54
        $this->fields               = FieldCollection::make(/** @scrutinizer ignore-type */ $fields);
Loading history...
55 37
        $this->removeCallbackMethod = $removeCallbackMethod;
56
57 37
        $this->key = is_null($key) ? null : $this->generateValidLayoutKey($key);
58 37
        $this->setRawAttributes($this->setEmptyValuesToNull($attributes));
59
    }
60
61
    /**
62
     * Resolve and return the result
63
     *
64
     * @return array
65
     */
66 4
    public function getResolved(): array
67
    {
68 4
        $this->resolve();
69
70 4
        return $this->getResolvedValue();
71
    }
72
73
    /**
74
     * Resolve the field for display and return the result.
75
     *
76
     * @return array
77
     */
78 4
    public function getResolvedForDisplay()
79
    {
80 4
        return $this->resolveForDisplay($this->getAttributes());
81
    }
82
83
    /**
84
     * @inerhitDoc
85
     */
86 12
    public function duplicate(?string $key, array $attributes = []): static
87
    {
88 12
        $fields = $this->fieldsCollection()->map(function ($field) {
89 12
            return $this->cloneField($field);
90 12
        });
91
92 12
        $clone = new static(
93 12
            $this->title(),
94 12
            $this->name(),
95 12
            $fields,
96 12
            $key,
97 12
            $attributes,
98 12
            $this->removeCallbackMethod,
99 12
        );
100 12
        $clone->useLimit($this->limit());
101 12
        $clone->setModel($this->model);
0 ignored issues
show
Deprecated Code introduced by
The function NovaFlexibleContent\Layouts\Layout::setModel() has been deprecated: I have no Idea where this used, and why we should keep it? ( Ignorable by Annotation )

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

101
        /** @scrutinizer ignore-deprecated */ $clone->setModel($this->model);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
102
103 12
        return $clone;
104
    }
105
106
    /**
107
     * Create a working field clone instance
108
     *
109
     * @param \Laravel\Nova\Fields\Field $original
110
     * @return \Laravel\Nova\Fields\Field
111
     */
112 12
    protected function cloneField(Field $original)
113
    {
114 12
        $field = clone $original;
115
116 12
        $callables = ['displayCallback', 'resolveCallback', 'fillCallback', 'requiredCallback'];
117
118 12
        foreach ($callables as $callable) {
119 12
            if (!is_a($field->$callable ?? null, \Closure::class)) {
120 12
                continue;
121
            }
122 4
            $field->$callable = $field->$callable->bindTo($field);
123
        }
124
125 12
        return $field;
126
    }
127
128
    /**
129
     * Resolve fields using given attributes.
130
     *
131
     * @param bool $empty
132
     * @return static
133
     */
134 6
    public function resolve(bool $empty = false): static
135
    {
136 6
        $this->fieldsCollection()->each(function ($field) use ($empty) {
137 4
            $field->resolve($empty ? $this->duplicate($this->inUseKey()) : $this);
138 6
        });
139
140 6
        return $this;
141
    }
142
143
    /**
144
     * Resolve fields for display using given attributes.
145
     */
146 4
    public function resolveForDisplay(array $attributes = []): array
147
    {
148 4
        $this->fieldsCollection()->each(function ($field) use ($attributes) {
149 4
            $field->resolveForDisplay($attributes);
150 4
        });
151
152 4
        return $this->getResolvedValue();
153
    }
154
155
    /**
156
     * Get the layout's resolved representation. Best used
157
     * after a resolve() call
158
     */
159 7
    public function getResolvedValue(): array
160
    {
161 7
        return [
162 7
            'layout' => $this->name(),
163
164 7
            'collapsed' => $this->isCollapsed(),
165
166
            // The (old) temporary key is preferred to the new one during
167
            // field resolving because we need to keep track of the current
168
            // attributes during the next fill request that will override
169
            // the key with a new, stronger & definitive one.
170 7
            'key' => $this->inUseKey(),
171
172
            // The layout's fields now temporarily contain the resolved
173
            // values from the current group's attributes. If multiple
174
            // groups use the same layout, the current values will be lost
175
            // since each group uses the same fields by reference. That's
176
            // why we need to serialize the field's current state.
177 7
            'attributes' => $this->fieldsCollection()->jsonSerialize(),
178 7
        ];
179
    }
180
181
    /**
182
     * Fill attributes using underlaying fields and incoming request.
183
     */
184 4
    public function fillFromRequest(ScopedRequest $request): array
185
    {
186 4
        return $this->fieldsCollection()->map(fn (Field $field) => $field->fill($request, $this))
187 4
            ->filter(fn ($callback) => is_callable($callback))
188 4
            ->values()
189 4
            ->all();
190
    }
191
192
    /**
193
     * Transform empty attribute values to null.
194
     */
195 37
    protected function setEmptyValuesToNull(array $dataArray = []): array
196
    {
197 37
        foreach ($dataArray as $key => $value) {
198 10
            if (!is_string($value) || strlen($value) > 0) {
199 10
                continue;
200
            }
201 2
            $dataArray[$key] = null;
202
        }
203
204 37
        return $dataArray;
205
    }
206
207
    /**
208
     * Transform layout for serialization.
209
     *
210
     * @return array
211
     */
212 2
    public function jsonSerialize(): array
213
    {
214
        // Calling an empty "resolve" first in order to empty all fields
215 2
        $this->resolve(true);
216
217 2
        return [
218 2
            'name'    => $this->name(),
219 2
            'title'   => $this->title(),
220 2
            'fields'  => $this->fieldsCollection()->jsonSerialize(),
221 2
            'limit'   => $this->limit(),
222 2
            'configs' => [
223 2
                'fieldUsedForDescription' => $this->fieldUsedForDescription(),
224 2
            ],
225 2
        ];
226
    }
227
}
228