Completed
Pull Request — master (#13)
by Maksim
12:56 queued 10:56
created

MultiUnitSupport::mutateAttribute()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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