-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFieldtypeObjectDimensions.module
More file actions
397 lines (346 loc) · 13.1 KB
/
FieldtypeObjectDimensions.module
File metadata and controls
397 lines (346 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
<?php
declare(strict_types=1);
namespace ProcessWire;
/*
* Fieldtype ObjectDimensions that stores length, width, height, volume(L*W*H) and area(L*W) data in the database
*
* Created by Jürgen K.
* https://github.com/juergenweb
* File name: FieldtypeObjectDimensions.module
* Created: 02.02.2022
*/
use Exception;
include_once('ObjectDimensions.php');
class FieldtypeObjectDimensions extends Fieldtype {
public function __construct() {
parent::__construct();
}
/**
* @return array
*/
public static function getModuleInfo():array {
return array(
'title' => 'Fieldtype Object Dimensions',
'summary' => 'Fieldtype to store length, width, height, volume and area of an object.',
'version' => '1.2.2',
'href' => 'https://github.com/juergenweb/FieldtypeObjectDimensions',
'icon' => 'cube',
'installs' => 'InputfieldObjectDimensions',
'requires' => [
'PHP>=8.0',
'ProcessWire>=3'
],
'author' => 'Jürgen Kern'
);
}
/**
* @return void
*/
public function init():void {
$this->addHookAfter('ProcessField::fieldSaved', $this, 'syncDatabase');
$this->addHookAfter('ProcessField::fieldSaved', $this, 'removeWidthVolumeValue');
}
/**
* After ProcessField::fieldSaved
* We need to hook field save to check if our database field (schema)is up-to-date
*
* @param HookEvent $event
* @return void
* @throws WireException
* @throws WirePermissionException
* @throws Exception
*/
public function syncDatabase(HookEvent $event):void {
$field = $event->arguments(0);
if ($field->type instanceof FieldtypeObjectDimensions) {
// Check if number of decimals is lower than the number of digits, because decimals are always part of
// the digits (otherwise we will get an MySQL error)
if ($field->input_decimals > 65) {
throw new Exception($this->_('The number of decimals must always be lower than the number of digits.'));
}
//… and now lets sync the schemas
$m = wire('modules')->get('FieldtypeObjectDimensions');
$result = $m->syncSchema($field);
// We just add a message if the database was actually modified
if ($result) {
$field->message($this->_('Database schema was updated.'));
}
}
}
/**
* After ProcessField::fieldSaved -> runs after saving the field configuration
* We need to hook field save to set height and volume to 0 in database if type was changed from 3d to 2d,
* because these properties do not exist on 2d
*
* @param HookEvent $event
* @return void
*/
public function removeWidthVolumeValue(HookEvent $event):void {
$field = $event->arguments(0);
if ($field->type instanceof FieldtypeObjectDimensions) {
if ($field->input_type == '2d') {
$database = $this->database;
$table = $field->getTable();
// update column width and volume to value 0
$query = "UPDATE `" . $database->escapeTable($table) . "` SET height = 0, volume = 0";
$database->query($query);
}
}
}
/**
* Link the core inputfield to this fieldtype
*
* @param Page $page
* @param Field $field
* @return null|_Module|Inputfield|Module
* @throws WirePermissionException
*
*/
public function getInputfield(Page $page, Field $field):null|_Module|Inputfield|Module {
return $this->modules->get('InputfieldObjectDimensions');
}
/**
* Sanitize the value
*
* @param Page $page
* @param Field $field
* @param int|object|WireArray|string $value
* @return int|null|ObjectDimensions|WireArray|string
*/
public function sanitizeValue(Page $page, Field $field, $value):int|null|ObjectDimensions|WireArray|string {
if (!$value instanceof ObjectDimensions) {
$value = $this->getBlankValue($page, $field);
}
// Track changes on dimension values
if ($value->isChanged('length') || $value->isChanged('width') || $value->isChanged('height')) {
$page->trackChange($field->name);
}
return $value;
}
/**
* Convert from DB storage to API value for displaying on a template page
*
* @param Page $page
* @param Field $field
* @param string|int|array $value
* @return ObjectDimensions
*
*/
public function wakeupValue(Page $page, Field $field, $value):ObjectDimensions {
// instantiate a new object with blank values
$dim = $this->getBlankValue($page, $field);
// make unit callable
$dim->unit = (string)$field->input_sizeunit;
$dim->length = (float)$value['length'];
$dim->width = (float)$value['width'];
$dim->height = (float)$value['height'];
$dim->volume = (float)$value['volume'];
$dim->area = (float)$value['area'];
return $dim;
}
public function ___formatValue(Page $page, Field $field, $value) {
$unit = $field->input_sizeunit ? ' ' . $field->input_sizeunit : ' cm';
$areaUnit = $unit . '<sup>2</sup>';
$volumeUnit = $unit . '<sup>3</sup>';
$fields = ['length', 'width', 'height', 'area', 'volume'];
foreach ($fields as $name) {
// for the case if page has not been saved and the dimension values are null
if (is_null($value->$name)) {
$value->$name = 0; // set it to integer 0
}
// create right unit for each property
if (!isset(${$name . 'Unit'})) {
$unitName = $unit;
} else {
$unitName = ${$name . 'Unit'};
}
// prevent multiple addition of unit
if (str_contains((string)$value->$name, $unitName)) {
$value->$name = str_replace($unitName, '', (string)$value->$name);
}
// create ..Unformatted property (no unit)
$value->{$name . 'Unformatted'} = (float)$value->$name;
// add unit to default property (including unit)
$value->$name = $value->$name . $unitName;
//create ..Label property (including unit and label)
$value->{$name . 'Label'} = InputfieldObjectDimensions::getLabels()[$name] . ': ' . $value->$name;
}
return $value;
}
/**
* Instantiate a new instance of ObjectDimensions
*
* @param Page $page
* @param Field $field
* @return ObjectDimensions
*
*/
public function getBlankValue(Page $page, Field $field):ObjectDimensions {
return new ObjectDimensions();
}
/**
* Calculate the area from width and length
*
* @param int|float|null $length
* @param int|float|null $width
* @return float|int|null
*/
private function calculateArea(int|float|null $length, int|float|null $width):int|float|null {
// check if null value is present
if (is_null($length)) {
return null;
}
if (is_null($width)) {
return null;
}
return ($width * $length);
}
/**
* Calculate the volume from width, height and length
*
* @param int|float $length
* @param int|float $width
* @param int|float $height
* @param Field $field
* @return float|int
*/
private function calculateVolume(
int|float $length,
int|float $width,
int|float $height,
Field $field
):float|int {
if ($field->input_type == '3d') {
return ($length * $width * $height);
} else {
return 0;
}
}
/**
* Convert from API to DB storage value.
*
* @param Page $page
* @param Field $field
* @param string|int|array|object $value
* @return array
* @throws Exception
*/
public function sleepValue(Page $page, Field $field, $value):array {
// throw error if value is not of the right type
if (!$value instanceof ObjectDimensions) {
throw new Exception($this->_('Expecting an instance of ObjectDimensions'));
}
$sleepValue = array(
'length' => (float)$value->length,
'width' => (float)$value->width,
'height' => (float)$value->height,
);
// recalculate computed values if dimensions have changed or if database schema has changed
if ($value->isChanged('width') || $value->isChanged('height') || $value->isChanged('length')) {
$sleepValue['volume'] = $this->calculateVolume($sleepValue['length'], $sleepValue['width'],
$sleepValue['height'], $field);
$sleepValue['area'] = $this->calculateArea($sleepValue['length'], $sleepValue['width']);
}
return $sleepValue;
}
/**
* User is not allowed to change this fieldtype to another fieldtype
*
* @param Field $field
* @return null
*
*/
public function ___getCompatibleFieldtypes(Field $field):null {
return null;
}
/**
* Set the database schema for this field
*
* @param Field $field
* @return array
*/
public function getDatabaseSchema(Field $field):array {
$schema = parent::getDatabaseSchema($field);
$schema['length'] = 'DECIMAL(65, 2) NOT NULL default 0.00';
$schema['width'] = 'DECIMAL(65, 2) NOT NULL default 0.00';
$schema['height'] = 'DECIMAL(65, 2) NOT NULL default 0.00';
$schema['area'] = 'DECIMAL(65, 2) NOT NULL default 0.00';
$schema['volume'] = 'DECIMAL(65, 2) NOT NULL default 0.00';
return $schema;
}
/**
* Returns the type/signature of the field we should have set in the configuration form as a MySQL type
* (fe decimal(10,2))
* @param Field $field
* @return string
*
*/
public function getConfiguredType(Field $field):string {
if (intval($field->input_decimals) !== 0) { //set float schema
// These should always be integers anyway
return "decimal(65," . intval($field->input_decimals) . ")";
} else {
//number of decimals= 0, so set integer schema
return "int(65)"; // These should always be integers anyway
}
}
/**
* Compares the type/signature of the field we should have set in the configuration form against
* the schema set in the DB
*
* @param Field $field
* @return bool true if schema configuration settings was changed, false if not
*
* @throws Exception
*/
public function checkSchemas(Field $field):bool {
// Get the configured field signature
$type = $this->getConfiguredType($field);
// Compare the configured and active signatures
if (strcasecmp($type, $this->getActiveType($field)) !== 0) {
return true;
}
return false;
}
/**
* Sync signature from configuration (set by values of digits and decimals) with signature in the DB
* @param Field $field
* Alter table schema foreach dimension field in the DB
* @return bool
* @throws WireException
* @throws Exception
*/
public function syncSchema(Field $field):bool {
if ($this->checkSchemas($field)) {
$database = $this->database;
$table = $field->getTable();
// alter all dimension field columns to the new type
$columns = ['length', 'width', 'height', 'volume', 'area'];
foreach ($columns as $col) {
$query = "ALTER TABLE `" . $database->escapeTable($table) . "` MODIFY $col {$this->getConfiguredType($field) } NOT NULL";
$database->query($query);
}
return true;
}
return false;
}
/**
* Returns the active type/signature of the field
* @param Field $field
* @return string
* @throws WireException
* @throws Exception
*/
public function getActiveType(Field $field):string {
$database = $this->database;
$table = $field->getTable();
// We could use INFORMATION_SCHEMA too, but due to its (default) slowness nvm
// check only one dimension (in this case width) to get the old schema of the dimension fields
$query = "SHOW FIELDS FROM `" . $database->escapeTable($table) . "` LIKE 'width'";
$result = $database->query($query);
if (!$result->rowCount()) {
throw new Exception($this->_("Cannot determine the type of the field"));
}
return $result->fetchColumn(1); // Type
}
}