Passed
Push — ft/dynamic-attributes ( 48373f...169e79 )
by Ben
06:24
created

SavingFields::saveQueuedFields()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 28
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 28
rs 8.8333
c 0
b 0
f 0
cc 7
nc 12
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Thinktomorrow\Chief\Fields;
6
7
use Illuminate\Http\Request;
8
use Illuminate\Support\Str;
9
use Thinktomorrow\Chief\Fields\Types\Field;
10
use Thinktomorrow\Chief\Fields\Types\FieldType;
11
use Thinktomorrow\Chief\Fields\Types\FileField;
12
use Thinktomorrow\Chief\Fields\Types\ImageField;
13
use Thinktomorrow\Chief\Media\Application\FileFieldHandler;
14
use Thinktomorrow\Chief\Media\Application\ImageFieldHandler;
15
16
trait SavingFields
17
{
18
    // If there is a save<key>Field this has priority over the set<Key>Field methods
19
    protected $saveAssistantMethods = [];
20
    protected $saveMethods = [];
21
22
    protected $queued_translations = [];
23
24
    public function saveFields(Request $request)
25
    {
26
        foreach ($this->fieldsWithAssistantFields() as $field) {
0 ignored issues
show
Bug introduced by
It seems like fieldsWithAssistantFields() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

26
        foreach ($this->/** @scrutinizer ignore-call */ fieldsWithAssistantFields() as $field) {
Loading history...
27
28
            // Custom save methods
29
            if ($this->detectCustomSaveMethods($field)) {
30
                continue;
31
            }
32
33
            // Custom set methods - default is the generic setField() method.
34
            $methodName = 'set' . ucfirst(Str::camel($field->getKey())) . 'Field';
35
            (method_exists($this, $methodName))
36
                ? $this->$methodName($field, $request)
37
                : $this->setField($field, $request);
38
        }
39
40
        // Save the model
41
        $this->saveQueuedFields();
42
43
        $this->saveQueuedMethods($request);
44
45
        // Attach the updated model to our manager.
46
        $this->manage($this->model);
0 ignored issues
show
Bug introduced by
It seems like manage() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

46
        $this->/** @scrutinizer ignore-call */ 
47
               manage($this->model);
Loading history...
47
    }
48
49
    protected function detectCustomSaveMethods(Field $field): bool
50
    {
51
        $saveMethodByKey = 'save' . ucfirst(Str::camel($field->getKey())) . 'Field';
52
        $saveMethodByType = 'save' . ucfirst(Str::camel($field->getType()->get())) . 'Fields';
53
54
        foreach ([$saveMethodByKey, $saveMethodByType] as $saveMethod) {
55
            foreach ($this->assistants() as $assistant) {
0 ignored issues
show
Bug introduced by
It seems like assistants() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

55
            foreach ($this->/** @scrutinizer ignore-call */ assistants() as $assistant) {
Loading history...
56
                if (method_exists($assistant, $saveMethod)) {
57
                    $this->saveAssistantMethods[$field->getKey()] = ['field'     => $field,
58
                                                                     'method'    => $saveMethod,
59
                                                                     'assistant' => $assistant,
60
                    ];
61
62
                    return true;
63
                }
64
            }
65
66
            if (method_exists($this, $saveMethod)) {
67
                $this->saveMethods[$field->getKey()] = ['field' => $field, 'method' => $saveMethod];
68
69
                return true;
70
            }
71
        }
72
73
        return false;
74
    }
75
76
    private function saveQueuedMethods($request)
77
    {
78
        foreach ($this->saveAssistantMethods as $data) {
79
            $method = $data['method'];
80
            $data['assistant']->$method($data['field'], $request);
81
        }
82
83
        foreach ($this->saveMethods as $data) {
84
            $method = $data['method'];
85
            $this->$method($data['field'], $request);
86
        }
87
    }
88
89
    public function setField(Field $field, Request $request)
90
    {
91
        // Is field set as translatable?
92
        if ($field->isTranslatable()) {
0 ignored issues
show
Bug introduced by
The method isTranslatable() 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

92
        if ($field->/** @scrutinizer ignore-call */ isTranslatable()) {
Loading history...
93
            if (!$this->requestContainsTranslations($request)) {
0 ignored issues
show
Bug introduced by
It seems like requestContainsTranslations() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

93
            if (!$this->/** @scrutinizer ignore-call */ requestContainsTranslations($request)) {
Loading history...
94
                return;
95
            }
96
97
            // Make our media fields able to be translatable as well...
98
            if ($field->ofType(FieldType::FILE, FieldType::IMAGE)) {
99
                throw new \Exception('Cannot process the ' . $field->getKey() . ' media field. Currently no support for translatable media files. We should fix this!');
100
            }
101
102
            // Okay so this is a bit odd but since all translations are expected to be inside the trans
103
            // array, we can add all these translations at once. Just make sure to keep track of the
104
            // keys since this is what our translation engine requires as well for proper update.
105
            $this->queued_translations = $request->input('trans');
106
            $this->translation_columns[] = $field->getColumn();
0 ignored issues
show
Bug Best Practice introduced by
The property translation_columns does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
107
108
            return;
109
        }
110
111
        // By default we assume the key matches the attribute / column naming
112
        $this->model->{$field->getColumn()} = $request->input($field->getKey());
113
    }
114
115
    private function saveQueuedFields()
116
    {
117
        $queued_translations = $this->queued_translations;
118
119
        foreach($queued_translations as $locale => $translations){
120
            foreach($translations as $key => $value) {
121
                if(method_exists($this->model, 'isDynamicAttributeKey') && $this->model->isDynamicAttributeKey($key)) {
122
                    $this->model->setDynamic($key, $value, $locale);
123
124
                    // Remove from queued translations
125
                    unset($queued_translations[$locale][$key]);
126
                }
127
128
                // remove any empty locale entries
129
                if(empty($queued_translations[$locale])) {
130
                    unset($queued_translations[$locale]);
131
                }
132
            }
133
        }
134
135
        $this->model->save();
136
137
        // Translations
138
        if (!empty($queued_translations)) {
139
            $this->saveTranslations($queued_translations, $this->model, $this->translation_columns);
0 ignored issues
show
Bug introduced by
It seems like saveTranslations() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

139
            $this->/** @scrutinizer ignore-call */ 
140
                   saveTranslations($queued_translations, $this->model, $this->translation_columns);
Loading history...
140
        }
141
142
        return (new static($this->registration))->manage($this->model);
0 ignored issues
show
Unused Code introduced by
The call to Thinktomorrow\Chief\Fiel...ngFields::__construct() has too many arguments starting with $this->registration. ( Ignorable by Annotation )

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

142
        return (/** @scrutinizer ignore-call */ new static($this->registration))->manage($this->model);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
143
    }
144
145
    public function saveFileFields(FileField $field, Request $request)
146
    {
147
        app(FileFieldHandler::class)->handle($this->model, $field, $request);
148
    }
149
150
    public function saveImageFields(ImageField $field, Request $request)
151
    {
152
        app(ImageFieldHandler::class)->handle($this->model, $field, $request);
153
    }
154
}
155