CoolCMS API

ColumnGate
in package

FinalYes

Length gate for form fields, measured by the column the field is written to.

Depending on the server sql_mode, a longer value is either silently truncated (non-strict) or fails the whole save with a database error (strict). The hydrator does not enforce limits and #[ORM\Column(length: ...)] is only a schema declaration, so the form is the only place where an overflow can be caught and shown on the field. The limit is read by reflection from the entity (ColumnLimits); a hand-copied number drifts from the schema sooner or later.

IMPORTANT: messages are global keys with the // prefix. Admin createForm() installs a PrefixedTranslator (see Form::useDomainTranslator()), so a relative key would be looked up in the presenter domain and would have to be copied into every module. The prefix bypasses the domain; messages live in admin.general.columnGate.*. Nette fills %d from the rule argument, so the translation shows the real column limit.

NOTE: rules are skipped for an optional empty field (Nette skips them in Rules::validate()), so the tax id pattern does not enforce filling; the caller's setRequired() does.

Usage:

ColumnGate::lengths($form, Order::class, ['sellerName', 'sellerStreet', 'sellerCity']);
ColumnGate::taxId($form->getComponent('customerRegNo'), Order::class, 'customerRegNo');

Table of Contents

Constants

ASSET_PATH_MESSAGE  : string = '//admin.general.columnGate.assetPath'
Message for an image path that is not local; global key.
DECIMAL_MESSAGE  : string = '//admin.general.columnGate.decimal'
Message for a number outside the column range; `%s` is the exclusive limit, filled by the caller.
FRONT_DECIMAL_MESSAGE  : string = '//front.general.columnGate.decimal'
Front end variant of {@see DECIMAL_MESSAGE}, see {@see FRONT_MAX_LENGTH_MESSAGE}.
FRONT_MAX_LENGTH_MESSAGE  : string = '//front.general.columnGate.maxLength'
Front end variant of the same message.
FRONT_NEGATIVE_PRICE_MESSAGE  : string = '//front.general.columnGate.negativePrice'
Front end message for a negative price (decimal gate without `$allowNegative`); without its own message a negative value got the range one ("enter a number less than …"), which is misleading.
FRONT_TAX_ID_MESSAGE  : string = '//front.general.columnGate.taxId'
Front end variant of {@see TAX_ID_MESSAGE}, see {@see FRONT_MAX_LENGTH_MESSAGE}.
HEX_COLOR_MESSAGE  : string = '//admin.general.columnGate.hexColor'
Message for a value that is not a hex color; global key.
HEX_COLOR_PATTERN  : string = '#[0-9A-Fa-f]{6}'
Hex color mask `#rrggbb`, exactly what fits the varchar(7) color columns.
INTEGER_MESSAGE  : string = '//admin.general.columnGate.integer'
Message for a whole number outside the column (or business) range; `%s` and `%s` are the inclusive bounds.
MAX_BYTES_MESSAGE  : string = '//admin.general.columnGate.maxBytes'
Message for a text longer than a `TEXT`-like column holds (bytes); `%d` is the byte limit.
MAX_LENGTH_MESSAGE  : string = '//admin.general.columnGate.maxLength'
Message for a value longer than the column holds; global key, `%d` is the column limit.
NEGATIVE_PRICE_MESSAGE  : string = '//admin.general.columnGate.negativePrice'
Admin message for a negative price (decimal gate without `$allowNegative`, passed as `$negativeMessage`); the range one alone ("must be less than …") misled for a negative value (admin walkthrough B5).
TAX_ID_MESSAGE  : string = '//admin.general.columnGate.taxId'
Message for a value that is not a tax id shape; global key.

Methods

assetPath()  : BaseControl
Legacy image path gate (`assetPath` of categories and regions): column length plus a local relative path only ({@see AssetPathPolicy}).
decimal()  : BaseControl
Range gate for decimal fields, measured by the column.
decimals()  : void
Batch variant of {@see decimal()}; see {@see lengths()} for the name matching rules.
guard()  : void
Column gate for a whole form: every text and number control is measured by the column it writes to, found by name in the given entities.
hexColor()  : BaseControl
Color gate: column length plus the `#rrggbb` mask.
integer()  : BaseControl
Range gate for integer fields, measured by the column ({@see ColumnLimits::integerRange()}).
length()  : BaseControl
Limits the control to the column width: client `maxlength` plus server rule `MAX_LENGTH`.
lengths()  : void
Batch variant for field blocks where the control name equals the entity property name.
taxId()  : BaseControl
Tax id gate: column length plus a loose shape mask ({@see TaxIdHelper::PATTERN}) that also lets foreign ids through.
taxIds()  : void
Batch variant of {@see taxId()}; control names equal entity property names.
columnError()  : array{0: string, 1: list}|null
columnFor()  : array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}|null
fillPlaceholders()  : string
Fills `%d` / `%s` placeholders in order (Nette does it for rule messages; a manual error has to).
guardedControls()  : array<int, BaseControl, 1: array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}}>
mappedLimit()  : array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}|null
Limits from the `#[ORM\Column]` mapping, in the {@see ColumnSchema::describe()} shape. A `text` column without `length` is `LONGTEXT` for Doctrine, so it gets no limit here.

Constants

ASSET_PATH_MESSAGE

Message for an image path that is not local; global key.

public string ASSET_PATH_MESSAGE = '//admin.general.columnGate.assetPath'

DECIMAL_MESSAGE

Message for a number outside the column range; `%s` is the exclusive limit, filled by the caller.

public string DECIMAL_MESSAGE = '//admin.general.columnGate.decimal'

FRONT_DECIMAL_MESSAGE

Front end variant of {@see DECIMAL_MESSAGE}, see {@see FRONT_MAX_LENGTH_MESSAGE}.

public string FRONT_DECIMAL_MESSAGE = '//front.general.columnGate.decimal'

FRONT_MAX_LENGTH_MESSAGE

Front end variant of the same message.

public string FRONT_MAX_LENGTH_MESSAGE = '//front.general.columnGate.maxLength'

IMPORTANT: a // key is absolute from the translation root, which differs between front end and admin. The admin key is not loaded on a front form, and a missing key crashes the page, so front end callers must pass this constant.

FRONT_NEGATIVE_PRICE_MESSAGE

Front end message for a negative price (decimal gate without `$allowNegative`); without its own message a negative value got the range one ("enter a number less than …"), which is misleading.

public string FRONT_NEGATIVE_PRICE_MESSAGE = '//front.general.columnGate.negativePrice'

FRONT_TAX_ID_MESSAGE

Front end variant of {@see TAX_ID_MESSAGE}, see {@see FRONT_MAX_LENGTH_MESSAGE}.

public string FRONT_TAX_ID_MESSAGE = '//front.general.columnGate.taxId'

HEX_COLOR_MESSAGE

Message for a value that is not a hex color; global key.

public string HEX_COLOR_MESSAGE = '//admin.general.columnGate.hexColor'

HEX_COLOR_PATTERN

Hex color mask `#rrggbb`, exactly what fits the varchar(7) color columns.

public string HEX_COLOR_PATTERN = '#[0-9A-Fa-f]{6}'

INTEGER_MESSAGE

Message for a whole number outside the column (or business) range; `%s` and `%s` are the inclusive bounds.

public string INTEGER_MESSAGE = '//admin.general.columnGate.integer'

MAX_BYTES_MESSAGE

Message for a text longer than a `TEXT`-like column holds (bytes); `%d` is the byte limit.

public string MAX_BYTES_MESSAGE = '//admin.general.columnGate.maxBytes'

MAX_LENGTH_MESSAGE

Message for a value longer than the column holds; global key, `%d` is the column limit.

public string MAX_LENGTH_MESSAGE = '//admin.general.columnGate.maxLength'

NEGATIVE_PRICE_MESSAGE

Admin message for a negative price (decimal gate without `$allowNegative`, passed as `$negativeMessage`); the range one alone ("must be less than …") misled for a negative value (admin walkthrough B5).

public string NEGATIVE_PRICE_MESSAGE = '//admin.general.columnGate.negativePrice'

TAX_ID_MESSAGE

Message for a value that is not a tax id shape; global key.

public string TAX_ID_MESSAGE = '//admin.general.columnGate.taxId'

Methods

assetPath()

Legacy image path gate (`assetPath` of categories and regions): column length plus a local relative path only ({@see AssetPathPolicy}).

public static assetPath(BaseControl $control, class-string $entity, string $property) : BaseControl

Without it an external https:// address was stored and public lists put it into <img src>. An empty optional field passes.

Parameters
$control : BaseControl
$entity : class-string
$property : string
Return values
BaseControl

decimal()

Range gate for decimal fields, measured by the column.

public static decimal(BaseControl $control, class-string $entity, string $property[, bool $allowNegative = false ][, string $message = self::DECIMAL_MESSAGE ][, string|null $negativeMessage = null ]) : BaseControl

IMPORTANT: neither a length gate nor Form::FLOAT is enough. FLOAT lets exponent notation (1e1000) through, (float) turns it into INF, number_format() into the string inf, which a strict sql_mode rejects with a generic database error and a non-strict one stores as 0.00. So both the shape (plain decimal notation) and the range are checked, and the range is measured on the value after rounding to the column scale, i.e. what is actually written; otherwise 9999.995 would pass as below the limit and be stored as 10000.00.

An empty optional field passes; the caller's setRequired() enforces filling.

Parameters
$control : BaseControl
$entity : class-string

entity whose column receives the write

$property : string
$allowNegative : bool = false

true only where a negative value makes sense (stock deduction)

$message : string = self::DECIMAL_MESSAGE
$negativeMessage : string|null = null

own message for a negative value (checked first); without it the range message covers negative values too

Return values
BaseControl

decimals()

Batch variant of {@see decimal()}; see {@see lengths()} for the name matching rules.

public static decimals(Container $container, class-string $entity, array<int, string> $properties[, bool $allowNegative = false ]) : void
Parameters
$container : Container
$entity : class-string
$properties : array<int, string>
$allowNegative : bool = false

guard()

Column gate for a whole form: every text and number control is measured by the column it writes to, found by name in the given entities.

public static guard(Form $form, array<string, array<int, class-string>> $containers[, array<int, string> $except = [] ][, ColumnSchema|null $schema = null ]) : void

A field without its own maxLength/range let an oversized value through validation; the save then failed in the database ("Data too long", "Out of range"), the admin saw a generic message and the entity manager stayed closed for the rest of the request (admin walkthrough A1, A14). Gating field by field missed new fields again and again, so the form declares only which entities its fields write to and every control is gated:

  • varchar(n)/char(n): at most n characters; TEXT-like: at most its size in bytes;
  • integer types: a whole number within the column (signed/unsigned);
  • decimal(p,s): a number within the column after rounding to the scale.

The limits are those of the real column (ColumnSchema: the mapping says which column, the database how big it is; mapping and schema differ in places, e.g. unsigned or tinyint). Without the schema reader (unit tests) the #[ORM\Column] mapping is used (ColumnLimits::describe()).

The name maps to a property directly (username), or without the language suffix (name_2 → the translation entity name). $containers maps a container path to its entities: '' is the form itself, a multiplier path ('answers') gates every row, a key ending with * takes the form's own controls with that name prefix ('quickPrice*': quickPriceTitle_2 → property title). Controls that already failed their own rules, disabled ones, hidden fields and controls in $except (a value transformed before storage, e.g. a password that is hashed) are skipped.

Runs in onValidate, so it also covers multiplier rows created from the POST; the maxlength HTML hint is set in onRender. An empty optional field passes; setRequired() enforces filling.

Parameters
$form : Form
$containers : array<string, array<int, class-string>>

container path => entities searched in order

$except : array<int, string> = []

control names never gated

$schema : ColumnSchema|null = null

hexColor()

Color gate: column length plus the `#rrggbb` mask.

public static hexColor(BaseControl $control, class-string $entity, string $property) : BaseControl

Color columns are varchar(7), exactly #rrggbb. Anything longer (#rrggbbaa, rgb(...), a color name) fails the save with a generic database error (strict sql_mode) or is silently truncated to seven characters and becomes a different color (non-strict), so the shape is checked too, not only the length. An empty optional field passes; the caller's setRequired() enforces filling.

Parameters
$control : BaseControl
$entity : class-string
$property : string
Return values
BaseControl

integer()

Range gate for integer fields, measured by the column ({@see ColumnLimits::integerRange()}).

public static integer(BaseControl $control, class-string $entity, string $property[, int|null $min = null ][, string $message = self::INTEGER_MESSAGE ]) : BaseControl

Form::INTEGER alone checks only the shape: 99999999999 or -1 into an unsigned int column failed the save with a generic database error (strict sql_mode) and the entity manager stayed closed for the rest of the request. $min narrows the lower bound where the business needs it (1 for a quantity), it never widens the column range.

Sets the min/max HTML attributes too (a browser hint on type=number). An empty optional field passes; the caller's setRequired() enforces filling.

Parameters
$control : BaseControl
$entity : class-string
$property : string
$min : int|null = null
$message : string = self::INTEGER_MESSAGE
Return values
BaseControl

length()

Limits the control to the column width: client `maxlength` plus server rule `MAX_LENGTH`.

public static length(BaseControl $control, class-string $entity, string $property[, string $message = self::MAX_LENGTH_MESSAGE ]) : BaseControl

maxlength alone is not enough: it is only a browser hint, ignored by a request sent outside the form, with JavaScript off or with edited HTML. The rule also catches pasted input.

Parameters
$control : BaseControl
$entity : class-string

entity whose column receives the write

$property : string
$message : string = self::MAX_LENGTH_MESSAGE
Return values
BaseControl

lengths()

Batch variant for field blocks where the control name equals the entity property name.

public static lengths(Container $container, class-string $entity, array<int, string> $properties) : void

A missing control is skipped because some blocks are built conditionally (for example VAT columns only for a VAT payer). A typo in a property name fails immediately in ColumnLimits on purpose: a silent gate that measures nothing is worse than none.

Parameters
$container : Container
$entity : class-string
$properties : array<int, string>

taxId()

Tax id gate: column length plus a loose shape mask ({@see TaxIdHelper::PATTERN}) that also lets foreign ids through.

public static taxId(BaseControl $control, class-string $entity, string $property) : BaseControl

The checkout holds the same pair of rules (AddressFormTrait). Admin is the second path to the same columns and must not be looser, otherwise a tax value is truncated exactly where the checkout protected it.

Parameters
$control : BaseControl
$entity : class-string
$property : string
Return values
BaseControl

taxIds()

Batch variant of {@see taxId()}; control names equal entity property names.

public static taxIds(Container $container, class-string $entity, array<int, string> $properties) : void
Parameters
$container : Container
$entity : class-string
$properties : array<int, string>

columnError()

private static columnError(mixed $value, array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool} $limit) : array{0: string, 1: list}|null
Parameters
$value : mixed
$limit : array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}
Return values
array{0: string, 1: list}|null —

message and its placeholder values, null = fits

columnFor()

private static columnFor(string $name, array<int, class-string> $entities, ColumnSchema|null $schema) : array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}|null
Parameters
$name : string
$entities : array<int, class-string>
$schema : ColumnSchema|null
Return values
array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}|null

fillPlaceholders()

Fills `%d` / `%s` placeholders in order (Nette does it for rule messages; a manual error has to).

private static fillPlaceholders(string $text, array<int, string> $args) : string
Parameters
$text : string
$args : array<int, string>
Return values
string

guardedControls()

private static guardedControls(Form $form, array<string, array<int, class-string>> $containers, array<int, string> $except, ColumnSchema|null $schema) : array<int, BaseControl, 1: array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}}>
Parameters
$form : Form
$containers : array<string, array<int, class-string>>
$except : array<int, string>
$schema : ColumnSchema|null
Return values
array<int, BaseControl, 1: array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}}>

mappedLimit()

Limits from the `#[ORM\Column]` mapping, in the {@see ColumnSchema::describe()} shape. A `text` column without `length` is `LONGTEXT` for Doctrine, so it gets no limit here.

private static mappedLimit(class-string $entity, string $property) : array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}|null
Parameters
$entity : class-string
$property : string
Return values
array{kind: string, length: ?int, min: ?int, max: ?int, precision: ?int, scale: ?int, unsigned: bool}|null
On this page

Search results