Passed
Push — v1 ( 847c7e...650516 )
by Andrew
14:32 queued 10:54
created

Units::normalizeValue()   C

Complexity

Conditions 7
Paths 15

Size

Total Lines 36
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 36
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 21
nc 15
nop 2
1
<?php
2
/**
3
 * Units plugin for Craft CMS 3.x
4
 *
5
 * A plugin for handling physical quantities and the units of measure in which
6
 * they're represented.
7
 *
8
 * @link      https://nystudio107.com/
9
 * @copyright Copyright (c) 2018 nystudio107
10
 */
11
12
namespace nystudio107\units\fields;
13
14
use nystudio107\units\assetbundles\unitsfield\UnitsFieldAsset;
15
16
use nystudio107\units\helpers\ClassHelper;
17
use nystudio107\units\models\Settings;
18
use nystudio107\units\models\UnitsData;
19
use nystudio107\units\Units as UnitsPlugin;
20
use nystudio107\units\validators\EmbeddedUnitsDataValidator;
21
22
use Craft;
23
use craft\base\ElementInterface;
24
use craft\base\Field;
25
use craft\base\PreviewableFieldInterface;
26
use craft\helpers\Json;
27
use craft\i18n\Locale;
28
29
use yii\base\InvalidConfigException;
30
31
use PhpUnitsOfMeasure\AbstractPhysicalQuantity;
32
use PhpUnitsOfMeasure\PhysicalQuantity\Length;
33
34
/**
35
 * @author    nystudio107
36
 * @package   Units
37
 * @since     1.0.0
38
 */
39
class Units extends Field implements PreviewableFieldInterface
40
{
41
    // Static Methods
42
    // =========================================================================
43
44
    /**
45
     * @inheritdoc
46
     */
47
    public static function displayName(): string
48
    {
49
        return Craft::t('units', 'Units');
50
    }
51
52
    // Public Properties
53
    // =========================================================================
54
55
    /**
56
     * @var string The default fully qualified class name of the unit of measure
57
     */
58
    public $defaultUnitsClass;
59
60
    /**
61
     * @var float The default value of the unit of measure
62
     */
63
    public $defaultValue;
64
65
    /**
66
     * @var string The default units that the unit of measure is in
67
     */
68
    public $defaultUnits;
69
70
    /**
71
     * @var bool Whether the units the field can be changed
72
     */
73
    public $changeableUnits;
74
75
    /**
76
     * @var int|float The minimum allowed number
77
     */
78
    public $min;
79
80
    /**
81
     * @var int|float|null The maximum allowed number
82
     */
83
    public $max;
84
85
    /**
86
     * @var int The number of digits allowed after the decimal point
87
     */
88
    public $decimals;
89
90
    /**
91
     * @var int|null The size of the field
92
     */
93
    public $size;
94
95
    // Public Methods
96
    // =========================================================================
97
98
    /**
99
     * @inheritdoc
100
     */
101
    public function init()
102
    {
103
        parent::init();
104
        /** @var Settings $settings */
105
        if (UnitsPlugin::$plugin !== null) {
106
            $settings = UnitsPlugin::$plugin->getSettings();
107
            if (!empty($settings)) {
108
                $this->defaultUnitsClass = $this->defaultUnitsClass ?? $settings->defaultUnitsClass;
109
                $this->defaultValue = $this->defaultValue ?? $settings->defaultValue;
110
                $this->defaultUnits = $this->defaultUnits ?? $settings->defaultUnits;
111
                $this->changeableUnits = $this->changeableUnits ?? $settings->defaultChangeableUnits;
112
                $this->min = $this->min ?? $settings->defaultMin;
113
                $this->max = $this->max ?? $settings->defaultMax;
114
                $this->decimals = $this->decimals ?? $settings->defaultDecimals;
115
                $this->size = $this->size ?? $settings->defaultSize;
116
            }
117
        }
118
    }
119
120
    /**
121
     * @inheritdoc
122
     */
123
    public function rules()
124
    {
125
        $rules = parent::rules();
126
        $rules = array_merge($rules, [
127
            ['defaultUnitsClass', 'string'],
128
            ['defaultValue', 'number'],
129
            ['defaultUnits', 'string'],
130
            ['changeableUnits', 'boolean'],
131
            [['min', 'max'], 'number'],
132
            [['decimals', 'size'], 'integer'],
133
            [
134
                ['max'],
135
                'compare',
136
                'compareAttribute' => 'min',
137
                'operator' => '>=',
138
            ],
139
        ]);
140
141
        if (!$this->decimals) {
142
            $rules[] = [['min', 'max'], 'integer'];
143
        }
144
145
        return $rules;
146
    }
147
148
    /**
149
     * @inheritdoc
150
     */
151
    public function normalizeValue($value, ElementInterface $element = null)
152
    {
153
        if ($value instanceof UnitsData) {
154
            return $value;
155
        }
156
        // Default config
157
        $config = [
158
            'unitsClass' => $this->defaultUnitsClass,
159
            'value' => $this->defaultValue,
160
            'units' => $this->defaultUnits,
161
        ];
162
        // Handle incoming values potentially being JSON or an array
163
        if (!empty($value)) {
164
            // Handle a numeric value coming in (perhaps from a Number field)
165
            if (\is_numeric($value)) {
166
                $config['value'] = (float)$value;
167
            } elseif (\is_string($value)) {
168
                $config = Json::decodeIfJson($value);
169
            }
170
            if (\is_array($value)) {
171
                $config = array_merge($config, array_filter($value));
172
            }
173
        }
174
        // Typecast it to a float
175
        $config['value'] = (float)$config['value'];
176
        // Create and validate the model
177
        $unitsData = new UnitsData($config);
0 ignored issues
show
Bug introduced by
It seems like $config can also be of type string; however, parameter $config of nystudio107\units\models\UnitsData::__construct() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

177
        $unitsData = new UnitsData(/** @scrutinizer ignore-type */ $config);
Loading history...
178
        if (!$unitsData->validate()) {
179
            Craft::error(
180
                Craft::t('units', 'UnitsData failed validation: ')
181
                .print_r($unitsData->getErrors(), true),
182
                __METHOD__
183
            );
184
        }
185
186
        return $unitsData;
187
    }
188
189
    /**
190
     * @inheritdoc
191
     */
192
    public function getSettingsHtml()
193
    {
194
        $unitsClassMap = array_flip(ClassHelper::getClassesInNamespace(Length::class));
195
196
        // Render the settings template
197
        return Craft::$app->getView()->renderTemplate(
198
            'units/_components/fields/Units_settings',
199
            [
200
                'field' => $this,
201
                'unitsClassMap' => $unitsClassMap,
202
            ]
203
        );
204
    }
205
206
    /**
207
     * @inheritdoc
208
     */
209
    public function getInputHtml($value, ElementInterface $element = null): string
210
    {
211
        if ($value instanceof UnitsData) {
212
            // Register our asset bundle
213
            try {
214
                Craft::$app->getView()->registerAssetBundle(UnitsFieldAsset::class);
215
            } catch (InvalidConfigException $e) {
216
                Craft::error($e->getMessage(), __METHOD__);
217
            }
218
            $model = $value;
219
            $value = $model->value;
220
            $decimals = $this->decimals;
221
            // If decimals is 0 (or null, empty for whatever reason), don't run this
222
            if ($decimals) {
223
                $decimalSeparator = Craft::$app->getLocale()->getNumberSymbol(Locale::SYMBOL_DECIMAL_SEPARATOR);
224
                $value = number_format($value, $decimals, $decimalSeparator, '');
225
            }
226
            // Get our id and namespace
227
            $id = Craft::$app->getView()->formatInputId($this->handle);
0 ignored issues
show
Bug introduced by
It seems like $this->handle can also be of type null; however, parameter $inputName of craft\web\View::formatInputId() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

227
            $id = Craft::$app->getView()->formatInputId(/** @scrutinizer ignore-type */ $this->handle);
Loading history...
228
            $namespacedId = Craft::$app->getView()->namespaceInputId($id);
229
230
            // Variables to pass down to our field JavaScript to let it namespace properly
231
            $jsonVars = [
232
                'id' => $id,
233
                'name' => $this->handle,
234
                'namespace' => $namespacedId,
235
                'prefix' => Craft::$app->getView()->namespaceInputId(''),
236
            ];
237
            $jsonVars = Json::encode($jsonVars);
238
            Craft::$app->getView()->registerJs("$('#{$namespacedId}-field').UnitsUnits(".$jsonVars.");");
239
240
            // Render the input template
241
            return Craft::$app->getView()->renderTemplate(
242
                'units/_components/fields/Units_input',
243
                [
244
                    'name' => $this->handle,
245
                    'field' => $this,
246
                    'id' => $id,
247
                    'namespacedId' => $namespacedId,
248
                    'value' => $value,
249
                    'model' => $model,
250
                    'field' => $this,
251
                ]
252
            );
253
        }
254
255
        return '';
256
    }
257
258
    /**
259
     * @inheritdoc
260
     */
261
    public function getElementValidationRules(): array
262
    {
263
        return [
264
            [
265
                EmbeddedUnitsDataValidator::class,
266
                'units' => $this->defaultUnits,
267
                'integerOnly' => $this->decimals ? false : true,
268
                'min' => $this->min,
269
                'max' => $this->max,
270
            ],
271
        ];
272
    }
273
}
274