Issues (462)

src/fields/Units.php (57 issues)

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
The tag in position 1 should be the @copyright tag
Loading history...
9
 * @copyright Copyright (c) nystudio107
0 ignored issues
show
@copyright tag must contain a year and the name of the copyright holder
Loading history...
10
 */
0 ignored issues
show
PHP version not specified
Loading history...
Missing @category tag in file comment
Loading history...
Missing @package tag in file comment
Loading history...
Missing @author tag in file comment
Loading history...
Missing @license tag in file comment
Loading history...
11
12
namespace nystudio107\units\fields;
13
14
use Craft;
15
use craft\base\ElementInterface;
0 ignored issues
show
The type craft\base\ElementInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use craft\base\Field;
17
use craft\base\PreviewableFieldInterface;
18
use craft\helpers\Html;
19
use craft\helpers\Json;
20
use craft\i18n\Locale;
21
use GraphQL\Type\Definition\Type;
22
use nystudio107\units\assetbundles\unitsfield\UnitsFieldAsset;
23
use nystudio107\units\gql\types\generators\UnitsDataGenerator;
24
use nystudio107\units\helpers\ClassHelper;
25
use nystudio107\units\models\Settings;
26
use nystudio107\units\models\UnitsData;
27
use nystudio107\units\Units as UnitsPlugin;
28
use nystudio107\units\validators\EmbeddedUnitsDataValidator;
29
use PhpUnitsOfMeasure\PhysicalQuantity\Length;
30
use yii\base\InvalidConfigException;
31
32
/**
0 ignored issues
show
Missing short description in doc comment
Loading history...
33
 * @author    nystudio107
0 ignored issues
show
The tag in position 1 should be the @package tag
Loading history...
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
34
 * @package   Units
0 ignored issues
show
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
35
 * @since     1.0.0
0 ignored issues
show
The tag in position 3 should be the @author tag
Loading history...
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
36
 */
0 ignored issues
show
Missing @category tag in class comment
Loading history...
Missing @license tag in class comment
Loading history...
Missing @link tag in class comment
Loading history...
37
class Units extends Field implements PreviewableFieldInterface
38
{
39
    // Static Methods
40
    // =========================================================================
41
42
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
43
     * @var string The default fully qualified class name of the unit of measure
44
     */
45
    public string $defaultUnitsClass;
46
47
    // Public Properties
48
    // =========================================================================
49
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
50
     * @var ?float The default value of the unit of measure
51
     */
52
    public ?float $defaultValue = null;
53
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
54
     * @var ?string The default units that the unit of measure is in
55
     */
56
    public ?string $defaultUnits = null;
57
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
58
     * @var ?bool Whether the units the field can be changed
59
     */
60
    public ?bool $changeableUnits = null;
61
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
62
     * @var int|float|null The minimum allowed number
63
     */
64
    public int|float|null $min = null;
65
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
66
     * @var int|float|null The maximum allowed number
67
     */
68
    public int|float|null $max = null;
69
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
70
     * @var ?int The number of digits allowed after the decimal point
71
     */
72
    public ?int $decimals = null;
73
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
74
     * @var ?int The size of the field
75
     */
76
    public ?int $size = null;
77
78
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
79
     * @inheritdoc
80
     */
0 ignored issues
show
Missing @return tag in function comment
Loading history...
81
    public static function displayName(): string
82
    {
83
        return Craft::t('units', 'Units');
84
    }
85
86
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
87
     * @inheritdoc
88
     */
0 ignored issues
show
Missing @return tag in function comment
Loading history...
89
    public static function icon(): string
90
    {
91
        return 'scale-balanced';
92
    }
93
94
    // Public Methods
95
    // =========================================================================
96
97
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
98
     * @inheritdoc
99
     */
0 ignored issues
show
Missing @return tag in function comment
Loading history...
100
    public function init(): void
101
    {
102
        parent::init();
103
        if (UnitsPlugin::$plugin !== null) {
104
            /** @var Settings $settings */
0 ignored issues
show
The open comment tag must be the only content on the line
Loading history...
Missing short description in doc comment
Loading history...
The close comment tag must be the only content on the line
Loading history...
105
            $settings = UnitsPlugin::$plugin->getSettings();
106
            if ($settings !== null) {
107
                $this->defaultUnitsClass = $this->defaultUnitsClass ?? $settings->defaultUnitsClass;
108
                $this->defaultValue = $this->defaultValue ?? $settings->defaultValue;
109
                $this->defaultUnits = $this->defaultUnits ?? $settings->defaultUnits;
110
                $this->changeableUnits = $this->changeableUnits ?? $settings->defaultChangeableUnits;
111
                $this->min = $this->min ?? $settings->defaultMin;
112
                $this->max = $this->max ?? $settings->defaultMax;
113
                $this->decimals = $this->decimals ?? $settings->defaultDecimals;
114
                $this->size = $this->size ?? $settings->defaultSize;
115
            }
116
        }
117
    }
118
119
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
120
     * @inheritdoc
121
     */
0 ignored issues
show
Missing @return tag in function comment
Loading history...
122
    public function rules(): array
123
    {
124
        $rules = parent::rules();
125
        $rules = array_merge($rules, [
0 ignored issues
show
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
126
            ['defaultUnitsClass', 'string'],
127
            ['defaultValue', 'number'],
128
            ['defaultUnits', 'string'],
129
            ['changeableUnits', 'boolean'],
130
            [['min', 'max'], 'number'],
131
            [['decimals', 'size'], 'integer'],
132
            [
133
                ['max'],
134
                'compare',
135
                'compareAttribute' => 'min',
136
                'operator' => '>=',
137
            ],
138
        ]);
0 ignored issues
show
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...
139
140
        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...
141
            $rules[] = [['min', 'max'], 'integer'];
142
        }
143
144
        return $rules;
145
    }
146
147
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Parameter $element should have a doc-comment as per coding-style.
Loading history...
148
     * @inheritdoc
149
     */
0 ignored issues
show
Missing @return tag in function comment
Loading history...
150
    public function normalizeValue(mixed $value, ?ElementInterface $element = null): mixed
151
    {
152
        if ($value instanceof UnitsData) {
153
            return $value;
154
        }
155
        // Default config
156
        $config = [
157
            'unitsClass' => $this->defaultUnitsClass,
158
            'value' => $this->defaultValue,
159
            'units' => $this->defaultUnits,
160
        ];
161
        // Handle incoming values potentially being JSON or an array
162
        if (!empty($value)) {
163
            // Handle a numeric value coming in (perhaps from a Number field)
164
            if (is_numeric($value)) {
165
                $config['value'] = (float)$value;
166
            } elseif (is_string($value)) {
167
                $config = Json::decodeIfJson($value);
168
            }
169
            if (is_array($value)) {
170
                $config = array_merge($config, array_filter($value));
171
            }
172
        }
173
        // Typecast it to a float
174
        $config['value'] = (float)$config['value'];
175
        // Create and validate the model
176
        $unitsData = new UnitsData($config);
177
        if (!$unitsData->validate()) {
178
            Craft::error(
179
                Craft::t('units', 'UnitsData failed validation: ')
180
                . print_r($unitsData->getErrors(), true),
0 ignored issues
show
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

180
                . /** @scrutinizer ignore-type */ print_r($unitsData->getErrors(), true),
Loading history...
181
                __METHOD__
182
            );
183
        }
184
185
        return $unitsData;
186
    }
187
188
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
189
     * @inheritdoc
190
     */
0 ignored issues
show
Missing @return tag in function comment
Loading history...
191
    public function getSettingsHtml(): ?string
192
    {
193
        $unitsClassMap = array_flip(ClassHelper::getClassesInNamespace(Length::class));
194
195
        // Render the settings template
196
        return Craft::$app->getView()->renderTemplate(
197
            'units/_components/fields/Units_settings',
198
            [
199
                'field' => $this,
200
                'unitsClassMap' => $unitsClassMap,
201
            ]
202
        );
203
    }
204
205
    /**
0 ignored issues
show
Missing short description in doc comment
Loading history...
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Parameter $element should have a doc-comment as per coding-style.
Loading history...
206
     * @inheritdoc
207
     */
0 ignored issues
show
Missing @return tag in function comment
Loading history...
208
    public function getInputHtml(mixed $value, ?ElementInterface $element = null): string
209
    {
210
        if ($value instanceof UnitsData) {
211
            // Register our asset bundle
212
            try {
213
                Craft::$app->getView()->registerAssetBundle(UnitsFieldAsset::class);
214
            } catch (InvalidConfigException $e) {
215
                Craft::error($e->getMessage(), __METHOD__);
216
            }
217
            $model = $value;
218
            $value = $model->value;
219
            $decimals = $this->decimals;
220
            // If decimals is 0 (or null, empty for whatever reason), don't run this
221
            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...
222
                $decimalSeparator = Craft::$app->getLocale()->getNumberSymbol(Locale::SYMBOL_DECIMAL_SEPARATOR);
223
                $value = number_format($value, $decimals, $decimalSeparator, '');
0 ignored issues
show
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

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