Passed
Push — v4 ( e305b5...a396fd )
by Andrew
08:48 queued 04:01
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;
0 ignored issues
show
Bug introduced by
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
Coding Style introduced by
Missing short description in doc comment
Loading history...
33
 * @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...
34
 * @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...
35
 * @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...
36
 */
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...
37
class Units extends Field implements PreviewableFieldInterface
38
{
39
    // Static Methods
40
    // =========================================================================
41
42
    /**
0 ignored issues
show
Coding Style introduced by
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
Coding Style introduced by
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
Coding Style introduced by
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
Coding Style introduced by
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
Coding Style introduced by
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
Coding Style introduced by
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
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 ?int $decimals = null;
73
    /**
0 ignored issues
show
Coding Style introduced by
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
Coding Style introduced by
Missing short description in doc comment
Loading history...
79
     * @inheritdoc
80
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
81
    public static function displayName(): string
82
    {
83
        return Craft::t('units', 'Units');
84
    }
85
86
    // Public Methods
87
    // =========================================================================
88
89
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
90
     * @inheritdoc
91
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
92
    public function init(): void
93
    {
94
        parent::init();
95
        if (UnitsPlugin::$plugin !== null) {
96
            /** @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...
97
            $settings = UnitsPlugin::$plugin->getSettings();
98
            if ($settings !== null) {
99
                $this->defaultUnitsClass = $this->defaultUnitsClass ?? $settings->defaultUnitsClass;
100
                $this->defaultValue = $this->defaultValue ?? $settings->defaultValue;
101
                $this->defaultUnits = $this->defaultUnits ?? $settings->defaultUnits;
102
                $this->changeableUnits = $this->changeableUnits ?? $settings->defaultChangeableUnits;
103
                $this->min = $this->min ?? $settings->defaultMin;
104
                $this->max = $this->max ?? $settings->defaultMax;
105
                $this->decimals = $this->decimals ?? $settings->defaultDecimals;
106
                $this->size = $this->size ?? $settings->defaultSize;
107
            }
108
        }
109
    }
110
111
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
112
     * @inheritdoc
113
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
114
    public function rules(): array
115
    {
116
        $rules = parent::rules();
117
        $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...
118
            ['defaultUnitsClass', 'string'],
119
            ['defaultValue', 'number'],
120
            ['defaultUnits', 'string'],
121
            ['changeableUnits', 'boolean'],
122
            [['min', 'max'], 'number'],
123
            [['decimals', 'size'], 'integer'],
124
            [
125
                ['max'],
126
                'compare',
127
                'compareAttribute' => 'min',
128
                'operator' => '>=',
129
            ],
130
        ]);
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...
131
132
        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...
133
            $rules[] = [['min', 'max'], 'integer'];
134
        }
135
136
        return $rules;
137
    }
138
139
    /**
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...
140
     * @inheritdoc
141
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
142
    public function normalizeValue(mixed $value, ?ElementInterface $element = null): mixed
143
    {
144
        if ($value instanceof UnitsData) {
145
            return $value;
146
        }
147
        // Default config
148
        $config = [
149
            'unitsClass' => $this->defaultUnitsClass,
150
            'value' => $this->defaultValue,
151
            'units' => $this->defaultUnits,
152
        ];
153
        // Handle incoming values potentially being JSON or an array
154
        if (!empty($value)) {
155
            // Handle a numeric value coming in (perhaps from a Number field)
156
            if (is_numeric($value)) {
157
                $config['value'] = (float)$value;
158
            } elseif (is_string($value)) {
159
                $config = Json::decodeIfJson($value);
160
            }
161
            if (is_array($value)) {
162
                $config = array_merge($config, array_filter($value));
163
            }
164
        }
165
        // Typecast it to a float
166
        $config['value'] = (float)$config['value'];
167
        // Create and validate the model
168
        $unitsData = new UnitsData($config);
169
        if (!$unitsData->validate()) {
170
            Craft::error(
171
                Craft::t('units', 'UnitsData failed validation: ')
172
                . 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

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

215
                $value = number_format(/** @scrutinizer ignore-type */ $value, $decimals, $decimalSeparator, '');
Loading history...
216
            }
217
            // Get our id and namespace
218
            $id = Html::id($this->handle);
0 ignored issues
show
Bug introduced by
It seems like $this->handle can also be of type null; however, parameter $id of craft\helpers\Html::id() 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

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