Completed
Push — master ( f45a4b...1d3333 )
by Maksim
01:24 queued 10s
created

MultiUnitSupport::forgetUnitsInput()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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