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
![]() |
|||||
9 | * @copyright Copyright (c) nystudio107 |
||||
0 ignored issues
–
show
|
|||||
10 | */ |
||||
0 ignored issues
–
show
|
|||||
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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||
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
|
|||||
33 | * @author nystudio107 |
||||
0 ignored issues
–
show
Content of the @author tag must be in the form "Display Name <[email protected]>"
![]() |
|||||
34 | * @package Units |
||||
0 ignored issues
–
show
|
|||||
35 | * @since 1.0.0 |
||||
0 ignored issues
–
show
|
|||||
36 | */ |
||||
0 ignored issues
–
show
|
|||||
37 | class Units extends Field implements PreviewableFieldInterface |
||||
38 | { |
||||
39 | // Static Methods |
||||
40 | // ========================================================================= |
||||
41 | |||||
42 | /** |
||||
0 ignored issues
–
show
|
|||||
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
|
|||||
50 | * @var ?float The default value of the unit of measure |
||||
51 | */ |
||||
52 | public ?float $defaultValue = null; |
||||
53 | /** |
||||
0 ignored issues
–
show
|
|||||
54 | * @var ?string The default units that the unit of measure is in |
||||
55 | */ |
||||
56 | public ?string $defaultUnits = null; |
||||
57 | /** |
||||
0 ignored issues
–
show
|
|||||
58 | * @var ?bool Whether the units the field can be changed |
||||
59 | */ |
||||
60 | public ?bool $changeableUnits = null; |
||||
61 | /** |
||||
0 ignored issues
–
show
|
|||||
62 | * @var int|float|null The minimum allowed number |
||||
63 | */ |
||||
64 | public int|float|null $min = null; |
||||
65 | /** |
||||
0 ignored issues
–
show
|
|||||
66 | * @var int|float|null The maximum allowed number |
||||
67 | */ |
||||
68 | public int|float|null $max = null; |
||||
69 | /** |
||||
0 ignored issues
–
show
|
|||||
70 | * @var ?int The number of digits allowed after the decimal point |
||||
71 | */ |
||||
72 | public ?int $decimals = null; |
||||
73 | /** |
||||
0 ignored issues
–
show
|
|||||
74 | * @var ?int The size of the field |
||||
75 | */ |
||||
76 | public ?int $size = null; |
||||
77 | |||||
78 | /** |
||||
0 ignored issues
–
show
|
|||||
79 | * @inheritdoc |
||||
80 | */ |
||||
0 ignored issues
–
show
|
|||||
81 | public static function displayName(): string |
||||
82 | { |
||||
83 | return Craft::t('units', 'Units'); |
||||
84 | } |
||||
85 | |||||
86 | /** |
||||
0 ignored issues
–
show
|
|||||
87 | * @inheritdoc |
||||
88 | */ |
||||
0 ignored issues
–
show
|
|||||
89 | public static function icon(): string |
||||
90 | { |
||||
91 | return 'scale-balanced'; |
||||
92 | } |
||||
93 | |||||
94 | // Public Methods |
||||
95 | // ========================================================================= |
||||
96 | |||||
97 | /** |
||||
0 ignored issues
–
show
|
|||||
98 | * @inheritdoc |
||||
99 | */ |
||||
0 ignored issues
–
show
|
|||||
100 | public function init(): void |
||||
101 | { |
||||
102 | parent::init(); |
||||
103 | if (UnitsPlugin::$plugin !== null) { |
||||
104 | /** @var Settings $settings */ |
||||
0 ignored issues
–
show
|
|||||
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
|
|||||
120 | * @inheritdoc |
||||
121 | */ |
||||
0 ignored issues
–
show
|
|||||
122 | public function rules(): array |
||||
123 | { |
||||
124 | $rules = parent::rules(); |
||||
125 | $rules = array_merge($rules, [ |
||||
0 ignored issues
–
show
|
|||||
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.
![]() |
|||||
139 | |||||
140 | if (!$this->decimals) { |
||||
0 ignored issues
–
show
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 For 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
![]() |
|||||
141 | $rules[] = [['min', 'max'], 'integer']; |
||||
142 | } |
||||
143 | |||||
144 | return $rules; |
||||
145 | } |
||||
146 | |||||
147 | /** |
||||
0 ignored issues
–
show
|
|||||
148 | * @inheritdoc |
||||
149 | */ |
||||
0 ignored issues
–
show
|
|||||
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
![]() |
|||||
181 | __METHOD__ |
||||
182 | ); |
||||
183 | } |
||||
184 | |||||
185 | return $unitsData; |
||||
186 | } |
||||
187 | |||||
188 | /** |
||||
0 ignored issues
–
show
|
|||||
189 | * @inheritdoc |
||||
190 | */ |
||||
0 ignored issues
–
show
|
|||||
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
|
|||||
206 | * @inheritdoc |
||||
207 | */ |
||||
0 ignored issues
–
show
|
|||||
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
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 For 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
![]() |
|||||
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
![]() |
|||||
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
|
|||||
257 | * @inheritdoc |
||||
258 | */ |
||||
0 ignored issues
–
show
|
|||||
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
|
|||||
271 | * @inheritdoc |
||||
272 | */ |
||||
0 ignored issues
–
show
|
|||||
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 |