Completed
Push — master ( 5d0f9f...7d804c )
by Maksim
12:58
created

MultiUnitSupport::setMultiUnitFieldSelectedUnit()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 22

Duplication

Lines 10
Ratio 45.45 %

Importance

Changes 0
Metric Value
dl 10
loc 22
rs 9.2568
c 0
b 0
f 0
cc 5
nc 7
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),
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
            //prevent saving of unit columns
83
            foreach ($model->getUnitConversionUnitColumns() as $unitColumn) {
0 ignored issues
show
Bug introduced by
The method getUnitConversionUnitColumns 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...
84
                if (isset($model->attributes[$unitColumn])) {
85
                    unset($model->attributes[$unitColumn]);
86
                }
87
            }
88
        });
89
        static::updating(function ($model) {
90
            /**
91
             * @var Model|MultiUnitSupport $model
92
             */
93
            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...
94
                $model->{$unitBasedColumn.$model->getUnitConversionDataPostfix()} = json_encode($model->calculateMultiUnitConversionData($model->getDirty()[$unitBasedColumn], new $options['default_unit'](), $options['supported_units']));
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...
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...
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...
95
            }
96
        });
97
    }
98
99
    /**
100
     * @param              $value
101
     * @param AbstractUnit $unit
102
     * @param string[]     $requiredUnits
103
     *
104
     * @return array
105
     */
106
    private function calculateMultiUnitConversionData($value, AbstractUnit $unit, $requiredUnits)
107
    {
108
        $conversionData = [];
109
        foreach ($requiredUnits as $requiredUnitClass) {
110
            /**
111
             * @var AbstractUnit $requiredUnit
112
             */
113
            $requiredUnit = new $requiredUnitClass();
114
            $conversionData[$requiredUnit->getId()] = (new $unit($value))->as($requiredUnit);
115
        }
116
117
        return $conversionData;
118
    }
119
120
    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...
121
    {
122
        return json_decode($this->{$field.$this->getUnitConversionDataPostfix()} ?? null);
123
    }
124
125
    /**
126
     * @return string
127
     */
128
    public function getUnitAttributePostfix()
129
    {
130
        return $this->unitAttributePostfix;
131
    }
132
133
    /**
134
     * @return string
135
     */
136
    protected function getUnitConversionDataPostfix()
137
    {
138
        return $this->unitConversionDataPostfix;
139
    }
140
141
    /**
142
     * @return array
143
     */
144
    public function getMultiUnitColumns()
145
    {
146
        return $this->multiUnitColumns;
147
    }
148
149
    /**
150
     * @param $field
151
     *
152
     * @throws NotSupportedMultiUnitField
153
     *
154
     * @return AbstractUnit[]
155
     */
156
    public function getMultiUnitFieldSupportedUnits($field)
157
    {
158
        if ($this->isMultiUnitField($field)) {
159
            return $this->getMultiUnitColumns()[$field]['supported_units'];
160
        }
161
162
        throw new NotSupportedMultiUnitField($field);
163
    }
164
165
    /**
166
     * @param $field
167
     *
168
     * @throws NotSupportedMultiUnitField
169
     *
170
     * @return AbstractUnit
171
     */
172
    public function getMultiUnitFieldDefaultUnit($field)
173
    {
174
        if ($this->isMultiUnitField($field)) {
175
            $unitClass = $this->getMultiUnitColumns()[$field]['default_unit'];
176
177
            return new $unitClass();
178
        }
179
180
        throw new NotSupportedMultiUnitField($field);
181
    }
182
183
    /**
184
     * @param $field
185
     *
186
     * @throws NotSupportedMultiUnitField
187
     *
188
     * @return AbstractUnit
189
     */
190
    public function getMultiUnitFieldSelectedUnit($field)
191
    {
192
        if ($this->isMultiUnitField($field)) {
193
            $unitClass = $this->multiUnitSelectedUnits[$field] ?? $this->getMultiUnitFieldDefaultUnit($field);
194
195
            return new $unitClass();
196
        }
197
198
        throw new NotSupportedMultiUnitField($field);
199
    }
200
201
    /**
202
     * @param $field
203
     * @param string $unit
204
     *
205
     * @throws NotSupportedMultiUnitField
206
     * @throws NotSupportedMultiUnitFieldUnit
207
     */
208
    public function setMultiUnitFieldSelectedUnit($field, $unit)
209
    {
210
        if ($this->isMultiUnitField($field)) {
211
            $found = false;
212 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...
213
                /**
214
                 * @var AbstractUnit $unit
215
                 */
216
                $supportedUnit = new $unitClass();
217
                if (strtolower($supportedUnit->getId()) == strtolower($unit)) {
218
                    $found = true;
219
                    break;
220
                }
221
            }
222
            if($found)
223
                $this->multiUnitSelectedUnits[$field] = $unitClass;
0 ignored issues
show
Bug introduced by
The variable $unitClass seems to be defined by a foreach iteration on line 212. 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...
224
            else
225
                throw new NotSupportedMultiUnitFieldUnit($field, $unit);
226
        }
227
        else
228
            throw new NotSupportedMultiUnitField($field);
229
    }
230
231
232
    /**
233
     * @param        $field
234
     * @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...
235
     *
236
     * @throws NotSupportedMultiUnitField
237
     *
238
     * @return mixed
239
     */
240
    public function getMultiUnitFieldValueByUnitName($field, $unit = null)
241
    {
242
        if ($this->isMultiUnitField($field)) {
243
            if (isset($this->{$field})) {
244
                if (is_null($unit)) {
245
                    $unit = $this->getMultiUnitFieldUnit($field);
246
                } else {
247 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...
248
                        /**
249
                         * @var AbstractUnit $unit
250
                         */
251
                        $supportedUnit = new $unitClass();
252
                        if (strtolower($supportedUnit->getId()) == strtolower($unit)) {
253
                            $unit = $supportedUnit;
254
                            break;
255
                        }
256
                    }
257
                }
258
                if (is_string($unit)) {
259
                    throw new NotSupportedMultiUnitField($field);
260
                }
261
                $existingConversionData = $this->getMultiUnitExistingConversionData($field);
262 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...
263
                    return $existingConversionData->{$unit->getId()};
264
                }
265
266
                return ($this->getMultiUnitFieldDefaultUnit($field)->setValue($this->{$field} ?? $this->attributes[$field]))->as(new $unit());
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...
267
            } else {
268
                return;
269
            }
270
        }
271
272
        throw new NotSupportedMultiUnitField($field);
273
    }
274
275
    /**
276
     * @param                   $field
277
     * @param AbstractUnit|null $unit
278
     *
279
     * @throws NotSupportedMultiUnitField
280
     *
281
     * @return mixed
282
     */
283
    public function getMultiUnitFieldValue($field, AbstractUnit $unit = null)
284
    {
285
        if ($this->isMultiUnitField($field)) {
286
            if (isset($this->{$field})) {
287
                if (is_null($unit)) {
288
                    $unit = $this->getMultiUnitFieldUnit($field);
289
                }
290
                $existingConversionData = $this->getMultiUnitExistingConversionData($field);
291 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...
292
                    return $existingConversionData->{$unit->getId()};
293
                }
294
295
                return ($this->getMultiUnitFieldDefaultUnit($field)->setValue($this->{$field} ?? $this->attributes[$field]))->as(new $unit());
296
            } else {
297
                return;
298
            }
299
        }
300
301
        throw new NotSupportedMultiUnitField($field);
302
    }
303
304
    protected function isMultiUnitField($field)
305
    {
306
        return isset($this->getMultiUnitColumns()[$field]);
307
    }
308
309
    /**
310
     * @param $field
311
     *
312
     * @throws NotSupportedMultiUnitField
313
     *
314
     * @return AbstractUnit
315
     */
316
    protected function getMultiUnitFieldUnit($field)
317
    {
318
        if (isset($this->{$field.$this->getUnitAttributePostfix()})) {
319
            foreach ($this->getMultiUnitFieldSupportedUnits($field) as $unitClass) {
320
                /**
321
                 * @var AbstractUnit $unit
322
                 */
323
                $unit = new $unitClass();
324
                if (strtolower($unit->getId()) == strtolower($this->{$field.$this->getUnitAttributePostfix()})) {
325
                    return $unit;
326
                }
327
            }
328
        }
329
330
        return $this->getMultiUnitFieldSelectedUnit($field);
331
    }
332
333
    protected function forgetMultiUnitFieldUnitInput($field)
334
    {
335
        //prevent column_units to by saved to DB
336
        if (isset($this->attributes[$field.$this->getUnitAttributePostfix()])) {
337
            $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...
338
        }
339
    }
340
341
    protected function setMultiUnitFieldUnit($field, AbstractUnit $unit)
342
    {
343
        $this->{$field.$this->getUnitAttributePostfix()} = $unit->getId();
344
        $this->forgetMultiUnitFieldUnitInput($field);
345
    }
346
347
    /**
348
     * @param $field
349
     *
350
     * @throws NotSupportedMultiUnitField
351
     */
352
    protected function resetMultiUnitFieldUnit($field)
353
    {
354
        $this->setMultiUnitFieldUnit($field, $this->getMultiUnitFieldDefaultUnit($field));
355
    }
356
357
    /**
358
     * Determine if a set mutator exists for an attribute.
359
     *
360
     * @param string $key
361
     *
362
     * @return bool
363
     */
364
    public function hasSetMutator($key)
365
    {
366
        if ($this->isMultiUnitField($key)) {
367
            return true;
368
        }
369
370
        return parent::hasSetMutator($key);
371
    }
372
373
    /**
374
     * Set the value of an attribute using its mutator.
375
     *
376
     * @param string $key
377
     * @param mixed  $value
378
     *
379
     * @throws NotSupportedMultiUnitField
380
     *
381
     * @return mixed
382
     */
383
    protected function setMutatedAttributeValue($key, $value)
384
    {
385
        if ($this->isMultiUnitField($key)) {
386
            $value = $this->processMultiUnitFieldChanges($key, $value);
387
            $this->attributes[$key] = $value;
388
389
            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...
390
                return parent::setMutatedAttributeValue($key, $value);
391
            }
392
393
            return $value;
394
        }
395
396
        parent::setMutatedAttributeValue($key, $value);
397
    }
398
399
    /**
400
     * Detect changes and set proper base value.
401
     *
402
     * @param $field
403
     * @param $value
404
     *
405
     * @throws NotSupportedMultiUnitField
406
     *
407
     * @return mixed
408
     */
409
    private function processMultiUnitFieldChanges($field, $value)
410
    {
411
        $existingConversionData = $this->getMultiUnitExistingConversionData($field);
412
        if (!is_null($existingConversionData)) {
413
            $inputUnit = $this->getMultiUnitFieldUnit($field);
414
            //change existing value only in case if new value doesn't match with stored conversion table or not exists
415
            if (!isset($existingConversionData->{$inputUnit->getId()}) || $value != $existingConversionData->{$inputUnit->getId()}) {
416
                $this->resetMultiUnitFieldUnit($field);
417
418
                return (new $inputUnit($value))->as($this->getMultiUnitFieldDefaultUnit($field));
419
            } elseif ($value == $existingConversionData->{$inputUnit->getId()}) {
420
                //forget changes if value actually isn't changed
421
                $this->resetMultiUnitFieldUnit($field);
422
                $originalValue = $existingConversionData->{$this->getMultiUnitFieldDefaultUnit($field)->getId()};
423
                $this->attributes[$field] = $originalValue;
424
                $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...
425
426
                return $originalValue;
427
            }
428
            $this->resetMultiUnitFieldUnit($field);
429
        }
430
431
        return $value;
432
    }
433
434
    /**
435
     * Determine if a get mutator exists for an attribute.
436
     *coo.
437
     *
438
     * @param string $key
439
     *
440
     * @return bool
441
     */
442
    public function hasGetMutator($key)
443
    {
444
        if ($this->isMultiUnitField($key) && isset($this->{$key})) {
445
            return true;
446
        }
447
448
        return parent::hasGetMutator($key);
449
    }
450
451
    /**
452
     * Get the value of an attribute using its mutator.
453
     *
454
     * @param string $key
455
     * @param mixed  $value
456
     *
457
     * @throws NotSupportedMultiUnitField
458
     *
459
     * @return mixed
460
     */
461
    public function mutateAttribute($key, $value)
462
    {
463
        if ($this->isMultiUnitField($key)) {
464
            $requestedUnit = $this->getMultiUnitFieldUnit($key);
465
466
            $value = $this->getMultiUnitFieldValue($key, new $requestedUnit());
467
            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...
468
                return parent::mutateAttribute($key, $value);
469
            }
470
471
            return $value;
472
        }
473
474
        return parent::mutateAttribute($key, $value);
475
    }
476
}
477