Passed
Push — v1 ( e19a8b...ad9c2f )
by Andrew
05:01 queued 14s
created

Units::getContentGqlType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 8
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * Units plugin for Craft CMS
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/
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
9
 * @copyright Copyright (c) nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
10
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
11
12
namespace nystudio107\units\fields;
13
14
use Craft;
15
use craft\base\ElementInterface;
16
use craft\base\Field;
17
use craft\base\PreviewableFieldInterface;
18
use craft\helpers\Json;
19
use craft\i18n\Locale;
20
use nystudio107\units\assetbundles\unitsfield\UnitsFieldAsset;
21
use nystudio107\units\gql\types\generators\UnitsDataGenerator;
22
use nystudio107\units\helpers\ClassHelper;
23
use nystudio107\units\models\Settings;
24
use nystudio107\units\models\UnitsData;
25
use nystudio107\units\Units as UnitsPlugin;
26
use nystudio107\units\validators\EmbeddedUnitsDataValidator;
27
use PhpUnitsOfMeasure\PhysicalQuantity\Length;
28
use yii\base\InvalidConfigException;
29
30
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
31
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
32
 * @package   Units
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
33
 * @since     1.0.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
34
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
35
class Units extends Field implements PreviewableFieldInterface
36
{
37
    // Public Properties
38
    // =========================================================================
39
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
40
     * @var ?string The default fully qualified class name of the unit of measure
41
     */
42
    public $defaultUnitsClass = null;
43
44
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
45
     * @var ?float The default value of the unit of measure
46
     */
47
    public $defaultValue = null;
48
49
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
50
     * @var ?string The default units that the unit of measure is in
51
     */
52
    public $defaultUnits = null;
53
54
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
55
     * @var ?bool Whether the units the field can be changed
56
     */
57
    public $changeableUnits = null;
58
59
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
60
     * @var int|float|null The minimum allowed number
61
     */
62
    public $min = null;
63
64
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
65
     * @var int|float|null The maximum allowed number
66
     */
67
    public $max = null;
68
69
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
70
     * @var ?int The number of digits allowed after the decimal point
71
     */
72
    public $decimals = null;
73
74
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
75
     * @var ?int The size of the field
76
     */
77
    public $size = null;
78
79
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
80
     * @inheritdoc
81
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
82
    public static function displayName(): string
83
    {
84
        return Craft::t('units', 'Units');
85
    }
86
87
    // Public Methods
88
    // =========================================================================
89
90
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
91
     * @inheritdoc
92
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
93
    public function init()
94
    {
95
        parent::init();
96
        if (UnitsPlugin::$plugin !== null) {
97
            /** @var ?Settings $settings */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
98
            $settings = UnitsPlugin::$plugin->getSettings();
99
            if ($settings !== null) {
100
                $this->defaultUnitsClass = $this->defaultUnitsClass ?? $settings->defaultUnitsClass;
101
                $this->defaultValue = $this->defaultValue ?? $settings->defaultValue;
102
                $this->defaultUnits = $this->defaultUnits ?? $settings->defaultUnits;
103
                $this->changeableUnits = $this->changeableUnits ?? $settings->defaultChangeableUnits;
104
                $this->min = $this->min ?? $settings->defaultMin;
105
                $this->max = $this->max ?? $settings->defaultMax;
106
                $this->decimals = $this->decimals ?? $settings->defaultDecimals;
107
                $this->size = $this->size ?? $settings->defaultSize;
108
            }
109
        }
110
    }
111
112
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
113
     * @inheritdoc
114
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
115
    public function rules()
116
    {
117
        $rules = parent::rules();
118
        $rules = array_merge($rules, [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
119
            ['defaultUnitsClass', 'string'],
120
            ['defaultValue', 'number'],
121
            ['defaultUnits', 'string'],
122
            ['changeableUnits', 'boolean'],
123
            [['min', 'max'], 'number'],
124
            [['decimals', 'size'], 'integer'],
125
            [
126
                ['max'],
127
                'compare',
128
                'compareAttribute' => 'min',
129
                'operator' => '>=',
130
            ],
131
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
132
133
        if (!$this->decimals) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->decimals of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
134
            $rules[] = [['min', 'max'], 'integer'];
135
        }
136
137
        return $rules;
138
    }
139
140
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
141
     * @inheritdoc
142
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
143
    public function normalizeValue($value, ElementInterface $element = null)
144
    {
145
        if ($value instanceof UnitsData) {
146
            return $value;
147
        }
148
        // Default config
149
        $config = [
150
            'unitsClass' => $this->defaultUnitsClass,
151
            'value' => $this->defaultValue,
152
            'units' => $this->defaultUnits,
153
        ];
154
        // Handle incoming values potentially being JSON or an array
155
        if (!empty($value)) {
156
            // Handle a numeric value coming in (perhaps from a Number field)
157
            if (is_numeric($value)) {
158
                $config['value'] = (float)$value;
159
            } elseif (is_string($value)) {
160
                $config = Json::decodeIfJson($value);
161
            }
162
            if (is_array($value)) {
163
                $config = array_merge($config, array_filter($value));
0 ignored issues
show
Bug introduced by
It seems like $config can also be of type null and string; however, parameter $arrays of array_merge() 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

163
                $config = array_merge(/** @scrutinizer ignore-type */ $config, array_filter($value));
Loading history...
164
            }
165
        }
166
        // Typecast it to a float
167
        $config['value'] = (float)$config['value'];
168
        // Create and validate the model
169
        $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

169
        $unitsData = new UnitsData(/** @scrutinizer ignore-type */ $config);
Loading history...
170
        if (!$unitsData->validate()) {
171
            Craft::error(
172
                Craft::t('units', 'UnitsData failed validation: ')
173
                . print_r($unitsData->getErrors(), true),
0 ignored issues
show
Bug introduced by
Are you sure print_r($unitsData->getErrors(), true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

173
                . /** @scrutinizer ignore-type */ print_r($unitsData->getErrors(), true),
Loading history...
174
                __METHOD__
175
            );
176
        }
177
178
        return $unitsData;
179
    }
180
181
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
182
     * @inheritdoc
183
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
184
    public function getSettingsHtml()
185
    {
186
        $unitsClassMap = array_flip(ClassHelper::getClassesInNamespace(Length::class));
187
188
        // Render the settings template
189
        return Craft::$app->getView()->renderTemplate(
190
            'units/_components/fields/Units_settings',
191
            [
192
                'field' => $this,
193
                'unitsClassMap' => $unitsClassMap,
194
            ]
195
        );
196
    }
197
198
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
199
     * @inheritdoc
200
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
201
    public function getInputHtml($value, ElementInterface $element = null): string
202
    {
203
        if ($value instanceof UnitsData) {
204
            // Register our asset bundle
205
            try {
206
                Craft::$app->getView()->registerAssetBundle(UnitsFieldAsset::class);
207
            } catch (InvalidConfigException $e) {
208
                Craft::error($e->getMessage(), __METHOD__);
209
            }
210
            $model = $value;
211
            $value = $model->value;
212
            $decimals = $this->decimals;
213
            // If decimals is 0 (or null, empty for whatever reason), don't run this
214
            if ($decimals) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $decimals of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
215
                $decimalSeparator = Craft::$app->getLocale()->getNumberSymbol(Locale::SYMBOL_DECIMAL_SEPARATOR);
216
                $value = number_format($value, $decimals, $decimalSeparator, '');
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $num of number_format() does only seem to accept double, 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

216
                $value = number_format(/** @scrutinizer ignore-type */ $value, $decimals, $decimalSeparator, '');
Loading history...
217
            }
218
            // Get our id and namespace
219
            $id = Craft::$app->getView()->formatInputId($this->handle);
220
            $namespacedId = Craft::$app->getView()->namespaceInputId($id);
221
222
            // Variables to pass down to our field JavaScript to let it namespace properly
223
            $jsonVars = [
224
                'id' => $id,
225
                'name' => $this->handle,
226
                'namespace' => $namespacedId,
227
                'prefix' => Craft::$app->getView()->namespaceInputId(''),
228
            ];
229
            $jsonVars = Json::encode($jsonVars);
230
            Craft::$app->getView()->registerJs("$('#{$namespacedId}-field').UnitsUnits(" . $jsonVars . ");");
231
232
            // Render the input template
233
            return Craft::$app->getView()->renderTemplate(
234
                'units/_components/fields/Units_input',
235
                [
236
                    'name' => $this->handle,
237
                    'field' => $this,
238
                    'id' => $id,
239
                    'namespacedId' => $namespacedId,
240
                    'value' => $value,
241
                    'model' => $model,
242
                ]
243
            );
244
        }
245
246
        return '';
247
    }
248
249
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
250
     * @inheritdoc
251
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
252
    public function getContentGqlType()
253
    {
254
        $typeArray = UnitsDataGenerator::generateTypes($this);
255
256
        return [
257
            'name' => $this->handle,
258
            'description' => 'Units field',
259
            'type' => array_shift($typeArray),
260
        ];
261
    }
262
263
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
264
     * @inheritdoc
265
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
266
    public function getElementValidationRules(): array
267
    {
268
        return [
269
            [
270
                EmbeddedUnitsDataValidator::class,
271
                'units' => $this->defaultUnits,
272
                'integerOnly' => $this->decimals ? false : true,
273
                'min' => $this->min,
274
                'max' => $this->max,
275
            ],
276
        ];
277
    }
278
}
279