Completed
Push — master ( 5061e5...8b9d92 )
by Maksim
11:42
created

MultiUnitSupport::processMultiUnitFieldChanges()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.8497
c 0
b 0
f 0
cc 6
nc 5
nop 2
1
<?php
2
3
namespace MaksimM\MultiUnitModels\Traits;
4
5
use Illuminate\Database\Eloquent\Model;
6
use Illuminate\Support\Arr;
7
use MaksimM\MultiUnitModels\Exceptions\NotSupportedMultiUnitField;
8
use MaksimM\MultiUnitModels\Exceptions\NotSupportedMultiUnitFieldUnit;
9
use UnitConverter\Unit\AbstractUnit;
10
11
trait MultiUnitSupport
12
{
13
    protected $unitAttributePostfix = '_units';
14
    protected $unitConversionDataPostfix = '_ucd';
15
    protected $multiUnitColumns = [];
16
    protected $multiUnitSelectedUnits = [];
17
18
    private function getUnitConversionDataColumns()
19
    {
20
        return array_map(function ($column) {
21
            return $column.$this->getUnitConversionDataPostfix();
22
        }, array_keys($this->getMultiUnitColumns()));
23
    }
24
25
    private function getUnitConversionUnitColumns()
26
    {
27
        return array_map(function ($column) {
28
            return $column.$this->getUnitAttributePostfix();
29
        }, array_keys($this->getMultiUnitColumns()));
30
    }
31
32
    /**
33
     * Allows to set input units and process them before multi-unit field.
34
     *
35
     * @param array $attributes
36
     *
37
     * @return array
38
     */
39
    protected function fillableFromArray(array $attributes)
40
    {
41
        return array_merge(array_intersect_key($attributes, array_flip($this->getUnitConversionUnitColumns())), parent::fillableFromArray($attributes));
42
    }
43
44
    /**
45
     * @return array
46
     */
47
    public function getFillable()
48
    {
49
        return array_merge($this->getUnitConversionDataColumns(), $this->getUnitConversionUnitColumns(), parent::getFillable());
50
    }
51
52
    /**
53
     * @return mixed
54
     */
55
    public function getHidden()
56
    {
57
        return array_merge(parent::getHidden(), $this->getUnitConversionDataColumns());
58
    }
59
60
    protected static function bootMultiUnitSupport()
61
    {
62
        //save conversion table if base value is changed
63
        static::creating(function ($model) {
64
            /**
65
             * @var Model|MultiUnitSupport $model
66
             */
67
            foreach ($model->getMultiUnitColumns() as $unitBasedColumn => $options) {
0 ignored issues
show
Bug introduced by
The method getMultiUnitColumns does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
68
                if (isset($model->attributes[$unitBasedColumn])) {
69
                    $model->{$unitBasedColumn.$model->getUnitConversionDataPostfix()} = json_encode(
0 ignored issues
show
Bug introduced by
The method getUnitConversionDataPostfix does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
70
                        $model->calculateMultiUnitConversionData(
0 ignored issues
show
Bug introduced by
The method calculateMultiUnitConversionData does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
71
                            $model->attributes[$unitBasedColumn],
72
                            $model->getMultiUnitFieldUnit($unitBasedColumn, true),
0 ignored issues
show
Bug introduced by
The method getMultiUnitFieldUnit does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
73
                            $options['supported_units']
74
                        )
75
                    );
76
                    $model->{$unitBasedColumn} = $model->processMultiUnitFieldChanges(
0 ignored issues
show
Bug introduced by
The method processMultiUnitFieldChanges does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
77
                        $unitBasedColumn,
78
                        $model->{$unitBasedColumn}
79
                    );
80
                }
81
            }
82
            $model->forgetUnitsInput();
0 ignored issues
show
Bug introduced by
The method forgetUnitsInput does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
83
        });
84
        static::updating(function ($model) {
85
            /**
86
             * @var Model|MultiUnitSupport $model
87
             */
88
            foreach (Arr::only($model->getMultiUnitColumns(), array_keys($model->getDirty())) as $unitBasedColumn => $options) {
0 ignored issues
show
Bug introduced by
The method getMultiUnitColumns does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
89
                $model->{$unitBasedColumn.$model->getUnitConversionDataPostfix()} = json_encode(
0 ignored issues
show
Bug introduced by
The method getUnitConversionDataPostfix does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
90
                    $model->calculateMultiUnitConversionData(
0 ignored issues
show
Bug introduced by
The method calculateMultiUnitConversionData does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
91
                        $model->getDirty()[$unitBasedColumn],
0 ignored issues
show
Bug introduced by
The method getDirty does only exist in Illuminate\Database\Eloquent\Model, but not in MaksimM\MultiUnitModels\Traits\MultiUnitSupport.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
92
                        $model->getMultiUnitFieldUnit($unitBasedColumn, true),
0 ignored issues
show
Bug introduced by
The method getMultiUnitFieldUnit does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
93
                        $options['supported_units']
94
                    )
95
                );
96
            }
97
            $model->forgetUnitsInput();
0 ignored issues
show
Bug introduced by
The method forgetUnitsInput does only exist in MaksimM\MultiUnitModels\Traits\MultiUnitSupport, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
98
        });
99
    }
100
101
    private function forgetUnitsInput(){
102
        //prevent saving of unit columns
103
        foreach ($this->getUnitConversionUnitColumns() as $unitColumn) {
104
            if (isset($this->attributes[$unitColumn])) {
0 ignored issues
show
Bug introduced by
The property attributes does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
105
                unset($this->attributes[$unitColumn]);
106
            }
107
        }
108
    }
109
110
    /**
111
     * @param              $value
112
     * @param AbstractUnit $unit
113
     * @param string[]     $requiredUnits
114
     *
115
     * @return array|null
116
     */
117
    private function calculateMultiUnitConversionData($value, AbstractUnit $unit, $requiredUnits)
118
    {
119
        if(is_null($value))
120
            return null;
121
122
        $conversionData = [];
123
        foreach ($requiredUnits as $requiredUnitClass) {
124
            /**
125
             * @var AbstractUnit $requiredUnit
126
             */
127
            $requiredUnit = new $requiredUnitClass();
128
            $conversionData[$requiredUnit->getId()] = (new $unit($value))->as($requiredUnit);
129
        }
130
131
        return $conversionData;
132
    }
133
134
    public function getMultiUnitExistingConversionData($field)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
135
    {
136
        return json_decode($this->{$field.$this->getUnitConversionDataPostfix()} ?? null);
137
    }
138
139
    /**
140
     * @return string
141
     */
142
    public function getUnitAttributePostfix()
143
    {
144
        return $this->unitAttributePostfix;
145
    }
146
147
    /**
148
     * @return string
149
     */
150
    protected function getUnitConversionDataPostfix()
151
    {
152
        return $this->unitConversionDataPostfix;
153
    }
154
155
    /**
156
     * @return array
157
     */
158
    public function getMultiUnitColumns()
159
    {
160
        return $this->multiUnitColumns;
161
    }
162
163
    /**
164
     * @param $field
165
     *
166
     * @throws NotSupportedMultiUnitField
167
     *
168
     * @return AbstractUnit[]
169
     */
170
    public function getMultiUnitFieldSupportedUnits($field)
171
    {
172
        if ($this->isMultiUnitField($field)) {
173
            return $this->getMultiUnitColumns()[$field]['supported_units'];
174
        }
175
176
        throw new NotSupportedMultiUnitField($field);
177
    }
178
179
    /**
180
     * @param $field
181
     *
182
     * @throws NotSupportedMultiUnitField
183
     *
184
     * @return AbstractUnit
185
     */
186
    public function getMultiUnitFieldDefaultUnit($field)
187
    {
188
        if ($this->isMultiUnitField($field)) {
189
            $unitClass = $this->getMultiUnitColumns()[$field]['default_unit'];
190
191
            return new $unitClass();
192
        }
193
194
        throw new NotSupportedMultiUnitField($field);
195
    }
196
197
    /**
198
     * @param $field
199
     *
200
     * @throws NotSupportedMultiUnitField
201
     *
202
     * @return AbstractUnit
203
     */
204
    public function getMultiUnitFieldSelectedUnit($field)
205
    {
206
        if ($this->isMultiUnitField($field)) {
207
            $unitClass = $this->multiUnitSelectedUnits[$field] ?? $this->getMultiUnitFieldDefaultUnit($field);
208
209
            return new $unitClass();
210
        }
211
212
        throw new NotSupportedMultiUnitField($field);
213
    }
214
215
    /**
216
     * @param $field
217
     * @param string $unit
218
     *
219
     * @throws NotSupportedMultiUnitField
220
     * @throws NotSupportedMultiUnitFieldUnit
221
     */
222
    public function setMultiUnitFieldSelectedUnit($field, $unit)
223
    {
224
        if ($this->isMultiUnitField($field)) {
225
            $found = false;
226 View Code Duplication
            foreach ($this->getMultiUnitFieldSupportedUnits($field) as $unitClass) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
227
                /**
228
                 * @var AbstractUnit $unit
229
                 */
230
                $supportedUnit = new $unitClass();
231
                if (strtolower($supportedUnit->getId()) == strtolower($unit)) {
232
                    $found = true;
233
                    break;
234
                }
235
            }
236
            if ($found) {
237
                $this->multiUnitSelectedUnits[$field] = $unitClass;
0 ignored issues
show
Bug introduced by
The variable $unitClass seems to be defined by a foreach iteration on line 226. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
238
            } else {
239
                throw new NotSupportedMultiUnitFieldUnit($field, $unit);
240
            }
241
        } else {
242
            throw new NotSupportedMultiUnitField($field);
243
        }
244
    }
245
246
    /**
247
     * @param        $field
248
     * @param string $unit
0 ignored issues
show
Documentation introduced by
Should the type for parameter $unit not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
249
     *
250
     * @throws NotSupportedMultiUnitField
251
     *
252
     * @return mixed
253
     */
254
    public function getMultiUnitFieldValueByUnitName($field, $unit = null)
255
    {
256
        if ($this->isMultiUnitField($field)) {
257
            if (isset($this->{$field})) {
258
                if (is_null($unit)) {
259
                    $unit = $this->getMultiUnitFieldUnit($field);
260
                } else {
261 View Code Duplication
                    foreach ($this->getMultiUnitFieldSupportedUnits($field) as $unitClass) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
262
                        /**
263
                         * @var AbstractUnit $unit
264
                         */
265
                        $supportedUnit = new $unitClass();
266
                        if (strtolower($supportedUnit->getId()) == strtolower($unit)) {
267
                            $unit = $supportedUnit;
268
                            break;
269
                        }
270
                    }
271
                }
272
                if (is_string($unit)) {
273
                    throw new NotSupportedMultiUnitField($field);
274
                }
275
                $existingConversionData = $this->getMultiUnitExistingConversionData($field);
276 View Code Duplication
                if (!is_null($existingConversionData) && !is_null($existingConversionData->{$unit->getId()})) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
277
                    return $existingConversionData->{$unit->getId()};
278
                }
279
280
                return ($this->getMultiUnitFieldSelectedUnit($field)->setValue($this->{$field} ?? $this->attributes[$field]))->as(new $unit());
281
            } else {
282
                return;
283
            }
284
        }
285
286
        throw new NotSupportedMultiUnitField($field);
287
    }
288
289
    /**
290
     * @param                   $field
291
     * @param AbstractUnit|null $unit
292
     *
293
     * @throws NotSupportedMultiUnitField
294
     *
295
     * @return mixed
296
     */
297
    public function getMultiUnitFieldValue($field, AbstractUnit $unit = null)
298
    {
299
        if ($this->isMultiUnitField($field)) {
300
            if (isset($this->{$field})) {
301
                if (is_null($unit)) {
302
                    $unit = $this->getMultiUnitFieldUnit($field);
303
                }
304
                $existingConversionData = $this->getMultiUnitExistingConversionData($field);
305 View Code Duplication
                if (!is_null($existingConversionData) && !is_null($existingConversionData->{$unit->getId()})) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306
                    return $existingConversionData->{$unit->getId()};
307
                }
308
309
                return ($this->getMultiUnitFieldSelectedUnit($field)->setValue($this->{$field} ?? $this->attributes[$field]))->as(new $unit());
310
            } else {
311
                return;
312
            }
313
        }
314
315
        throw new NotSupportedMultiUnitField($field);
316
    }
317
318
    protected function isMultiUnitField($field)
319
    {
320
        return isset($this->getMultiUnitColumns()[$field]);
321
    }
322
323
    /**
324
     * @param $field
325
     *
326
     * @throws NotSupportedMultiUnitField
327
     *
328
     * @return AbstractUnit
329
     */
330
    protected function getMultiUnitFieldUnit($field, $preferDefault = false)
331
    {
332
        if (isset($this->{$field.$this->getUnitAttributePostfix()})) {
333
            foreach ($this->getMultiUnitFieldSupportedUnits($field) as $unitClass) {
334
                /**
335
                 * @var AbstractUnit $unit
336
                 */
337
                $unit = new $unitClass();
338
                if (strtolower($unit->getId()) == strtolower($this->{$field.$this->getUnitAttributePostfix()})) {
339
                    return $unit;
340
                }
341
            }
342
        }
343
344
        return $preferDefault ? $this->getMultiUnitFieldDefaultUnit($field) : $this->getMultiUnitFieldSelectedUnit($field);
345
    }
346
347
    protected function forgetMultiUnitFieldUnitInput($field)
348
    {
349
        //prevent column_units to by saved to DB
350
        if (isset($this->attributes[$field.$this->getUnitAttributePostfix()])) {
351
            $this->syncOriginalAttribute($field.$this->getUnitAttributePostfix());
0 ignored issues
show
Bug introduced by
It seems like syncOriginalAttribute() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
352
        }
353
    }
354
355
    protected function setMultiUnitFieldUnit($field, AbstractUnit $unit)
356
    {
357
        if (isset($this->{$field.$this->getUnitAttributePostfix()})) {
358
            $this->{$field.$this->getUnitAttributePostfix()} = $unit->getId();
359
        }
360
        $this->forgetMultiUnitFieldUnitInput($field);
361
    }
362
363
    /**
364
     * @param $field
365
     *
366
     * @throws NotSupportedMultiUnitField
367
     */
368
    protected function resetMultiUnitFieldUnit($field)
369
    {
370
        $this->setMultiUnitFieldUnit($field, $this->getMultiUnitFieldSelectedUnit($field));
371
    }
372
373
    /**
374
     * Determine if a set mutator exists for an attribute.
375
     *
376
     * @param string $key
377
     *
378
     * @return bool
379
     */
380
    public function hasSetMutator($key)
381
    {
382
        if ($this->isMultiUnitField($key)) {
383
            return true;
384
        }
385
386
        return parent::hasSetMutator($key);
387
    }
388
389
    /**
390
     * Set the value of an attribute using its mutator.
391
     *
392
     * @param string $key
393
     * @param mixed  $value
394
     *
395
     * @throws NotSupportedMultiUnitField
396
     *
397
     * @return mixed
398
     */
399
    protected function setMutatedAttributeValue($key, $value)
400
    {
401
        if ($this->isMultiUnitField($key)) {
402
            $value = $this->processMultiUnitFieldChanges($key, $value);
403
            $this->attributes[$key] = $value;
404
405
            if (parent::hasSetMutator($key)) {
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (hasSetMutator() instead of setMutatedAttributeValue()). Are you sure this is correct? If so, you might want to change this to $this->hasSetMutator().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
406
                return parent::setMutatedAttributeValue($key, $value);
407
            }
408
409
            return $value;
410
        }
411
412
        parent::setMutatedAttributeValue($key, $value);
413
    }
414
415
    /**
416
     * Detect changes and set proper base value.
417
     *
418
     * @param $field
419
     * @param $value
420
     *
421
     * @throws NotSupportedMultiUnitField
422
     *
423
     * @return mixed
424
     */
425
    private function processMultiUnitFieldChanges($field, $value)
426
    {
427
        if(!is_null($value)) {
428
            $existingConversionData = $this->getMultiUnitExistingConversionData($field);
429
            if (!is_null($existingConversionData)) {
430
                $inputUnit = $this->getMultiUnitFieldUnit($field);
431
                //change existing value only in case if new value doesn't match with stored conversion table or not exists
432
                if (!isset(
433
                        $existingConversionData->{$inputUnit->getId()}
434
                    ) || $value != $existingConversionData->{$inputUnit->getId()}) {
435
                    $this->resetMultiUnitFieldUnit($field);
436
437
                    return (new $inputUnit($value))->as($this->getMultiUnitFieldDefaultUnit($field));
438
                } elseif ($value == $existingConversionData->{$inputUnit->getId()}) {
439
                    //forget changes if value actually isn't changed
440
                    $this->resetMultiUnitFieldUnit($field);
441
                    $originalValue = $existingConversionData->{$this->getMultiUnitFieldDefaultUnit($field)->getId()};
442
                    $this->attributes[$field] = $originalValue;
443
                    $this->syncOriginalAttribute($field);
0 ignored issues
show
Bug introduced by
It seems like syncOriginalAttribute() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
444
445
                    return $originalValue;
446
                }
447
                $this->resetMultiUnitFieldUnit($field);
448
            }
449
        }
450
451
        return $value;
452
    }
453
454
    /**
455
     * Determine if a get mutator exists for an attribute.
456
     *coo.
457
     *
458
     * @param string $key
459
     *
460
     * @return bool
461
     */
462
    public function hasGetMutator($key)
463
    {
464
        if ($this->isMultiUnitField($key) && isset($this->{$key})) {
465
            return true;
466
        }
467
468
        return parent::hasGetMutator($key);
469
    }
470
471
    /**
472
     * Get the value of an attribute using its mutator.
473
     *
474
     * @param string $key
475
     * @param mixed  $value
476
     *
477
     * @throws NotSupportedMultiUnitField
478
     *
479
     * @return mixed
480
     */
481
    public function mutateAttribute($key, $value)
482
    {
483
        if ($this->isMultiUnitField($key)) {
484
            $requestedUnit = $this->getMultiUnitFieldUnit($key);
485
486
            $value = $this->getMultiUnitFieldValue($key, new $requestedUnit());
487
            if (parent::hasGetMutator($key)) {
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (hasGetMutator() instead of mutateAttribute()). Are you sure this is correct? If so, you might want to change this to $this->hasGetMutator().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
488
                return parent::mutateAttribute($key, $value);
489
            }
490
491
            return $value;
492
        }
493
494
        return parent::mutateAttribute($key, $value);
495
    }
496
}
497