Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions formwork/fields/array.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,37 @@

return function (App $app) {
return [
'validate' => function (Field $field, $value) {
if (Constraint::isEmpty($value)) {
return [];
}
'methods' => [
/**
* Return whether the field is associative
*/
'isAssociative' => function (Field $field): bool {
return $field->is('associative', false);
},

if ($value instanceof Arrayable) {
$value = $value->toArray();
}
'validate' => function (Field $field, $value): array {
if (Constraint::isEmpty($value)) {
return [];
}

if ($value instanceof Arrayable) {
$value = $value->toArray();
}

if (!is_array($value)) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}
if (!is_array($value)) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}

if ($field->is('associative')) {
foreach (array_keys($value) as $key) {
if (is_int($key)) {
unset($value[$key]);
if ($field->isAssociative()) {
foreach (array_keys($value) as $key) {
if (is_int($key)) {
unset($value[$key]);
}
}
}
}

return array_filter($value);
},
return array_filter($value);
},
],
];
};
26 changes: 14 additions & 12 deletions formwork/fields/checkbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@

return function (App $app) {
return [
'validate' => function (Field $field, $value) {
if (Constraint::isTruthy($value)) {
return true;
}
'methods' => [
'validate' => function (Field $field, $value): bool {
if (Constraint::isTruthy($value)) {
return true;
}

if (Constraint::isFalsy($value)) {
return false;
}
if (Constraint::isFalsy($value)) {
return false;
}

if ($value === null) {
return false;
}
if ($value === null) {
return false;
}

throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
},
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
},
],
];
};
14 changes: 8 additions & 6 deletions formwork/fields/color.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

return function (App $app) {
return [
'validate' => function (Field $field, $value) {
if (!Constraint::matchesRegex($value, '/^#[0-9A-Fa-f]{6}$/')) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}
'methods' => [
'validate' => function (Field $field, $value): string {
if (!Constraint::matchesRegex($value, '/^#[0-9A-Fa-f]{6}$/')) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}

return strtolower($value);
},
return strtolower($value);
},
],
];
};
131 changes: 79 additions & 52 deletions formwork/fields/date.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,69 +9,96 @@

return function (App $app): array {
return [
/**
* By default the date is formatted using the YYYY-MM-DD format.
* This is at the same time human-readable and comparable when sorting.
*/
'format' => function (Field $field, string $format = 'YYYY-MM-DD', string $type = 'pattern') use ($app): string {
$translation = $app->translations()->getCurrent();
'methods' => [
/**
* Return the field value formatted as a date string
*
* By default the date is formatted using the `YYYY-MM-DD` format.
* This is at the same time human-readable and comparable when sorting.
*
* @param string $format the format to use for the date string
* @param string $type the type of format to use, either `pattern` or `date`
*/
'format' => function (Field $field, string $format = 'YYYY-MM-DD', string $type = 'pattern') use ($app): string {
$translation = $app->translations()->getCurrent();

$format = match (strtolower($type)) {
'pattern' => Date::patternToFormat($format),
'date' => $format,
default => throw new InvalidArgumentException('Invalid date format type'),
};
$format = match (strtolower($type)) {
'pattern' => Date::patternToFormat($format),
'date' => $format,
default => throw new InvalidArgumentException('Invalid date format type'),
};

return $field->isEmpty() ? '' : Date::formatTimestamp($field->toTimestamp(), $format, $translation);
},
return $field->isEmpty() ? '' : Date::formatTimestamp($field->toTimestamp(), $format, $translation);
},

'toTimestamp' => function (Field $field) use ($app): ?int {
$formats = [
$app->config()->get('system.date.dateFormat'),
$app->config()->get('system.date.datetimeFormat'),
];
return $field->isEmpty() ? null : Date::toTimestamp($field->value(), $formats);
},
/**
* Return the field value as a timestamp
*/
'toTimestamp' => function (Field $field) use ($app): ?int {
$formats = [
$app->config()->get('system.date.dateFormat'),
$app->config()->get('system.date.datetimeFormat'),
];
return $field->isEmpty() ? null : Date::toTimestamp($field->value(), $formats);
},

'toDuration' => function (Field $field) use ($app): string {
return $field->isEmpty() ? '' : Date::formatTimestampAsDistance($field->toTimestamp(), $app->translations()->getCurrent());
},
/**
* Return the field value as a duration string.
*
* The duration is formatted as a human-readable translated string representing
* the time difference between the field value and the current time.
*/
'toDuration' => function (Field $field) use ($app): string {
return $field->isEmpty() ? '' : Date::formatTimestampAsDistance($field->toTimestamp(), $app->translations()->getCurrent());
},

'toDateTimeString' => function (Field $field): string {
return $field->isEmpty() ? '' : Str::removeEnd($field->format('YYYY-MM-DD[T]hh:mm:ss'), ':00');
},
/**
* Return the field value as a date string in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format
*
* The time part is removed if the field does not have a time component.
*/
'toDateTimeString' => function (Field $field): string {
return $field->isEmpty() ? '' : Str::removeEnd($field->format('YYYY-MM-DD[T]hh:mm:ss'), ':00');
},

'toString' => function (Field $field): string {
return $field->isEmpty() ? '' : $field->format();
},
/**
* Return the field value as a formatted string
*/
'toString' => function (Field $field): string {
return $field->isEmpty() ? '' : $field->format();
},

'return' => function (Field $field): Field {
return $field;
},
'return' => function (Field $field): Field {
return $field;
},

'hasTime' => function (Field $field): bool {
return $field->is('time', true);
},
/**
* Return whether the field has a time component
*/
'hasTime' => function (Field $field): bool {
return $field->is('time', true);
},

'validate' => function (Field $field, $value) use ($app): ?string {
if (Constraint::isEmpty($value)) {
return null;
}
'validate' => function (Field $field, $value) use ($app): ?string {
if (Constraint::isEmpty($value)) {
return null;
}

$inputFormats = [
$app->config()->get('system.date.dateFormat'),
$app->config()->get('system.date.datetimeFormat'),
];
$inputFormats = [
$app->config()->get('system.date.dateFormat'),
$app->config()->get('system.date.datetimeFormat'),
];

$format = $field->hasTime()
? 'Y-m-d H:i:s'
: 'Y-m-d';
$format = $field->hasTime()
? 'Y-m-d H:i:s'
: 'Y-m-d';

try {
return date($format, Date::toTimestamp($value, $inputFormats));
} catch (InvalidArgumentException $e) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s":%s', $field->name(), $field->type(), Str::after($e->getMessage(), ':')));
}
},
try {
return date($format, Date::toTimestamp($value, $inputFormats));
} catch (InvalidArgumentException $e) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s":%s', $field->name(), $field->type(), Str::after($e->getMessage(), ':')));
}
},
],
];
};
62 changes: 45 additions & 17 deletions formwork/fields/duration.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,55 @@

return function (App $app) {
return [
'validate' => function (Field $field, $value): int|float {
if (!is_numeric($value)) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}
'methods' => [
/**
* Return the unit of the duration field
*
* This can be `seconds`, `minutes`, `hours`, `days`, `weeks`, `months`, or `years`.
*
* The default is `seconds`.
*/
'unit' => function (Field $field): string {
return $field->get('unit', 'seconds');
},

// This reliably casts numeric values to int or float
$value += 0;
/**
* Return the intervals of the duration field
*
* This is an array of intervals that can be used to display the duration in a more human-readable format.
*
* The default is `['days', 'hours', 'minutes', 'seconds']`.
*/
'intervals' => function (Field $field): array {
return $field->get('intervals', ['days', 'hours', 'minutes', 'seconds']);
},

if ($field->has('min') && $value < $field->get('min')) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" must be greater than or equal to %d', $field->name(), $field->type(), $field->get('min')));
}
'validate' => function (Field $field, $value): int|float {
if (!is_numeric($value)) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}

if ($field->has('max') && $value > $field->get('max')) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" must be less than or equal to %d', $field->name(), $field->type(), $field->get('max')));
}
// This reliably casts numeric values to int or float
$value += 0;

if ($field->has('step') && ($value - $field->get('min', 0)) % $field->get('step') !== 0) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" does not conform to the step value %d', $field->name(), $field->value(), $field->get('step')));
}
if ($field->has('min') && $value < $field->get('min')) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" must be greater than or equal to %d', $field->name(), $field->type(), $field->get('min')));
}

return $value;
},
if ($field->has('max') && $value > $field->get('max')) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" must be less than or equal to %d', $field->name(), $field->type(), $field->get('max')));
}

if ($field->has('step') && ($value - $field->get('min', 0)) % $field->get('step') !== 0) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" does not conform to the step value %d', $field->name(), $field->value(), $field->get('step')));
}

if (!in_array($field->unit(), ['seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'])) {
throw new ValidationException(sprintf('Invalid unit for field "%s" of type "%s"', $field->name(), $field->type()));
}

return $value;
},
],
];
};
57 changes: 30 additions & 27 deletions formwork/fields/email.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,35 @@

return function (App $app) {
return [
'validate' => function (Field $field, $value): string {
if (Constraint::isEmpty($value)) {
return '';
}

if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" is not a valid e-mail address', $field->name(), $field->value()));
}

if (!is_string($value)) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}

if ($field->has('min') && strlen($value) < $field->get('min')) {
throw new ValidationException(sprintf('The minimum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('min')));
}

if ($field->has('max') && strlen($value) > $field->get('max')) {
throw new ValidationException(sprintf('The maximum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->get('max')));
}

if ($field->has('pattern') && !Constraint::matchesRegex($value, $field->get('pattern'))) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" does not match the required pattern', $field->name(), $field->value()));
}

return $value;
},
'extend' => 'text',
'methods' => [
'validate' => function (Field $field, $value): string {
if (Constraint::isEmpty($value)) {
return '';
}

if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" is not a valid e-mail address', $field->name(), $field->value()));
}

if (!is_string($value)) {
throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type()));
}

if ($field->has('minLength') && strlen($value) < $field->minLength()) {
throw new ValidationException(sprintf('The minimum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->minLength()));
}

if ($field->has('maxLength') && strlen($value) > $field->maxLength()) {
throw new ValidationException(sprintf('The maximum allowed length for field "%s" of type "%s" is %d', $field->name(), $field->value(), $field->maxLength()));
}

if ($field->has('pattern') && !Constraint::matchesRegex($value, $field->pattern())) {
throw new ValidationException(sprintf('The value of field "%s" of type "%s" does not match the required pattern', $field->name(), $field->value()));
}

return $value;
},
],
];
};
Loading