Skip to content

Commit

Permalink
[UPDATE] initialization of fields and handling of expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
georgehristov committed Mar 25, 2020
1 parent 3bb2f57 commit 306bf88
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 97 deletions.
124 changes: 57 additions & 67 deletions src/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use atk4\core\TrackableTrait;
use atk4\dsql\Expression;
use atk4\dsql\Expressionable;
use atk4\core\InitializerTrait;

/**
* Class description?
Expand All @@ -18,6 +19,9 @@ class Field implements Expressionable
use TrackableTrait;
use DIContainerTrait;
use ReadableCaptionTrait;
use InitializerTrait {
init as _init;
}

// {{{ Properties

Expand Down Expand Up @@ -244,6 +248,14 @@ public function __construct($defaults = [])
}
}
}

/**
* Initialization.
*/
public function init()
{
$this->_init();
}

/**
* Validate and normalize value.
Expand All @@ -261,73 +273,20 @@ public function __construct($defaults = [])
*/
public function normalize($value)
{
// SQL fields are allowed to have expressions inside of them.
if ($value instanceof Expression ||
$value instanceof Expressionable) {
return $value;
}

// NULL value is always fine if it is allowed
if ($value === null) {
if ($value === null || $value === '') {
if ($this->required) {
throw new ValidationException([$this->name => 'Must not be null']);
throw new ValidationException([$this->name => 'Must not be null or empty']);
}

return;
}

$f = $this;

// only string type fields can use empty string as legit value, for all
// other field types empty value is the same as no-value, nothing or null
if ($f->type && $f->type != 'string' && $value === '') {
if ($this->required) {
throw new ValidationException([$this->name => 'Must not be empty']);
}

return;
}

// validate scalar values
if (in_array($f->type, ['string', 'text', 'integer', 'money', 'float']) && !is_scalar($value)) {
throw new ValidationException([$this->name => 'Must use scalar value']);
}

// normalize
// @TODO remove this block in future - it's useless
switch ($f->type) {
case null: // loose comparison, but is OK here
// NOTE - this is not always the same as type=string. Need to review what else it can be and how type=null is used at all
if ($this->required && empty($value)) {
throw new ValidationException([$this->name => 'Must not be empty']);
}
break;
case 'string':
throw new Exception(['Use Field\Line for type=string', 'this'=>$this]);
case 'text':
throw new Exception(['Use Field\Text for type=text', 'this'=>$this]);
case 'integer':
throw new Exception(['Use Field\Integer for type=integer', 'this'=>$this]);
case 'float':
throw new Exception(['Use Field\Numeric for type=float', 'this'=>$this]);
case 'money':
throw new Exception(['Use Field\Money for type=money', 'this'=>$this]);
case 'boolean':
throw new Exception(['Use Field\Boolean for type=boolean', 'this'=>$this]);
case 'date':
throw new Exception(['Use Field\Date for type=date', 'this'=>$this]);
case 'datetime':
throw new Exception(['Use Field\DateTime for type=datetime', 'this'=>$this]);
case 'time':
throw new Exception(['Use Field\Time for type=time', 'this'=>$this]);
case 'array':
throw new Exception(['Use Field\Array_ for type=array', 'this'=>$this]);
case 'object':
throw new Exception(['Use Field\Object_ for type=object', 'this'=>$this]);
}

return $value;
}

public static function isExpression($value)
{
return $value instanceof Expression || $value instanceof Expressionable;
}

/**
* Return array of seed properties of this Field object.
Expand Down Expand Up @@ -511,22 +470,48 @@ public function getCaption(): string

/**
* Returns typecasting callback if defined.
*
*
* Typecasting can be defined as (in order of precedence)
*
* * affects all typecasting for the field
* $user->addField('dob', ['Date', 'typecast'=>[$encode_fx, $decode_fx]]);
*
* * affects typecasting for specific persistence class
* $user->addField('dob', ['Date', 'persistence'=>['atk4\data\Persistence\SQL'=>['typecast'=>[$encode_fx, $decode_fx]]]]);
*
* * affects typecasting for all persistences
* $user->addField('dob', ['Date', 'persistence'=>['typecast'=>[$encode_fx, $decode_fx]]]);
*
* * default typecasting (if none of above set) will be used for all fields of the class defined in field methods
* typecastSave / typecastLoad based on the $mode
*
* @param string $mode - load|save
*
*
* @return callable|false
*/
public function getTypecaster($mode)
{
// map for backward compatibility with definition
// [typecast_save_callback, typecast_load_callback]
$map = [
'save' => 0,
'load' => 1,
'save' => 0,
'load' => 1
];

$fx = $this->typecast[$mode] ?? $this->typecast[$map[$mode]] ?? false;


$persistence = $this->getPersistence();

// persistence specific typecast
$specific = $persistence ? ($this->persistence[get_class($persistence)]['typecast'] ?? null) : null;

// get the typecast definition to be applied
// field specific or persistence specific or persistence general
$typecast = $this->typecast ?? $specific ?? $this->persistence['typecast'] ?? [];

// default typecaster is method in the field named typecastSave or typecastLoad if such method exists
$default = method_exists($this, 'typecast' . ucfirst($mode)) ? [$this, 'typecast' . ucfirst($mode)] : false;

$fx = $typecast[$mode] ?? $typecast[$map[$mode]] ?? $default;

return is_callable($fx) ? $fx : false;
}

Expand All @@ -550,6 +535,11 @@ public function getSerializer($mode)

return is_callable($fx) ? $fx : false;
}

public function getPersistence()
{
return $this->owner ? $this->owner->persistence : null;
}

// }}}

Expand Down
7 changes: 1 addition & 6 deletions src/Field/Boolean.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,13 @@

namespace atk4\data\Field;

use atk4\core\InitializerTrait;
use atk4\data\ValidationException;

/**
* Your favorite nullable binary type.
*/
class Boolean extends \atk4\data\Field
{
use InitializerTrait {
init as _init;
}

/** @var string Field type for backward compatibility. */
public $type = 'boolean';

Expand Down Expand Up @@ -52,7 +47,7 @@ class Boolean extends \atk4\data\Field
*/
public function init()
{
$this->_init();
parent::init();

// Backwards compatibility
if ($this->enum) {
Expand Down
12 changes: 3 additions & 9 deletions src/Field/Callback.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,11 @@

namespace atk4\data\Field;

use atk4\core\InitializerTrait;

/**
* Evaluate php expression after load.
*/
class Callback extends \atk4\data\Field
{
use InitializerTrait {
init as _init;
}

/**
* Method to execute for evaluation.
*
Expand Down Expand Up @@ -55,12 +49,12 @@ class Callback extends \atk4\data\Field
*/
public function init()
{
$this->_init();
parent::init();

$this->ui['table']['sortable'] = false;

$this->owner->onHook('afterLoad', function ($m) {
$m->data[$this->short_name] = call_user_func($this->fx ?: $this->expr, $m);
$this->owner->onHook('afterLoad', function ($model) {
$model->data[$this->short_name] = call_user_func($this->fx ?: $this->expr, $model);
});
}
}
6 changes: 1 addition & 5 deletions src/Field/Line.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ class Line extends Text
*/
public function normalize($value)
{
$value = parent::normalize($value);

// remove all line-ends
$value = trim(str_replace(["\r", "\n"], '', $value));

return $value;
return trim(str_replace(["\r", "\n"], '', parent::normalize($value)));
}
}
16 changes: 8 additions & 8 deletions src/Field/Numeric.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,26 +102,26 @@ public function normalize($value)
/**
* Round up to the nearest number.
*
* @param float $n Number
* @param int $p Precision
* @param float $nunber Number
* @param int $precision Precision
*
* @return float
*/
protected function roundUp(float $n, int $p): float
protected function roundUp(float $nunber, int $precision): float
{
return $p ? ceil($n / $p) * $p : ceil($n);
return $precision ? ceil($nunber / $precision) * $precision : ceil($nunber);
}

/**
* Round down to the nearest number.
*
* @param float $n Number
* @param int $p Precision
* @param float $number Number
* @param int $precision Precision
*
* @return float
*/
protected function roundDown(float $n, int $p): float
protected function roundDown(float $number, int $precision): float
{
return $p ? floor($n / $p) * $p : floor($n);
return $precision ? floor($number / $precision) * $precision : floor($number);
}
}
2 changes: 1 addition & 1 deletion src/Field/Object_.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function normalize($value)
{
if ($value === null || $value === '') {
if ($this->required) {
throw new ValidationException([$this->name => 'Must not be null']);
throw new ValidationException([$this->name => 'Must not be null or empty']);
}

return;
Expand Down
2 changes: 1 addition & 1 deletion src/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ public function set($field, $value = null)
&& $this->hook('normalize', [$f, $value]) !== false
&& $this->strict_types
) {
$value = $f->normalize($value);
$value = $f->isExpression($value) ? $value : $f->normalize($value);
}
} catch (Exception $e) {
$e->addMoreInfo('field', $field);
Expand Down

0 comments on commit 306bf88

Please sign in to comment.