public $defaultValue;
public $autoIncrement;
+ private $validator;
+ private $formatter;
+
public function __construct(array $column)
{
$this->name = $column['Field'];
}
}
+ public function registerFormatter($class)
+ {
+ $this->formatter = $class;
+ }
+
+ public function registerValidator($class)
+ {
+ $this->validator = $class;
+ }
+
public function format($value, $badNullFallbackToDefault = false)
{
if (is_null($value)) {
return $this->defaultValue;
}
throw new PlDBBadValueException($value, $this, 'null not allowed');
+ }
+ if (!is_null($this->validator)) {
+ $class = $this->validator;
+ new $class($this, $value);
+ }
+ if (!is_null($this->formatter)) {
+ $class = $this->formatter;
+ $value = new $class($this, $value);
} else if ($this->type == 'enum') {
if (!$this->typeParameters->hasFlag($value)) {
throw new PlDBBadValueException($value, $this, 'invalid value for enum ' . $this->typeParameters->flags());
}
/* TODO: Check bounds */
return $value;
- } else if ($this->type == 'varchar') {
+ } else if (ends_with($this->type, 'char')) {
if (strlen($value) > $this->typeLength) {
throw new PlDBBadValueException($value, $this, 'value is expected to be at most ' . $this->typeLength . ' characters long, ' . strlen($value) . ' given');
}
return $value;
- } else if ($this->type == 'char') {
- if (strlen($value) != $this->typeLength) {
- throw new PlDBBadValueException($value, $this, 'value is expected to be ' . $this->typeLength . ' characters long, ' . strlen($value) . ' given');
- }
- return $value;
} else if (starts_with($this->type, 'date') || $this->type == 'timestamp') {
- $date = $value;
- $value = make_datetime($value);
- if (is_null($value)) {
- throw new PlDBBadValueException($date, $this, 'value is expected to be a date/time, ' . $date . ' given');
- }
- if ($this->type == 'date') {
- $value = new DateFormatter($value, 'Y-m-d');
- } else if ($this->type == 'datetime') {
- $value = new DateFormatter($value, 'Y-m-d H:i:s');
- } else {
- $value = new DateFormatter($value, 'U');
- }
+ return new DateFieldFormatter($this, $value);
}
return $value;
}
}
-class DateFormatter implements XDBFormat
+interface PlDBTableFieldValidator
+{
+ public function __construct(PlDBTableField $field, $value);
+}
+
+interface PlDBTableFieldFormatter extends PlDBTableFieldValidator, XDBFormat, PlExportable
+{
+}
+
+class DateFieldFormatter implements PlDBTableFieldFormatter
{
private $datetime;
private $storageFormat;
- public function __construct(DateTime $date, $storageFormat)
+ public function __construct(PlDBTableField $field, $date)
{
- $this->datetime = $date;
- $this->storageFormat = $storageFormat;
+ $this->datetime = make_datetime($date);
+ if (is_null($this->datetime)) {
+ throw new PlDBBadValueException($date, $field, 'value is expected to be a date/time, ' . $date . ' given');
+ }
+ if ($field->type == 'date') {
+ $this->storageFormat = 'Y-m-d';
+ } else if ($field->type == 'datetime') {
+ $this->storageFormat = 'Y-m-d H:i:s';
+ } else {
+ $this->storageFormat = 'U';
+ }
}
public function format()
{
- return XDB::escape($this->datetime->format($this->storageFormat));
+ return XDB::escape($this->export());
}
public function date($format)
{
return $this->datetime->format($format);
}
+
+ public function export()
+ {
+ return $this->datetime->format($this->storageFormat);
+ }
+}
+
+class JSonFieldFormatter implements PlDBTableFieldFormatter, ArrayAccess
+{
+ private $data;
+
+ public function __construct(PlDBTableField $field, $data)
+ {
+ if (strpos($field->type, 'text') === false) {
+ throw new PlDBBadValueException($data, $field, 'json formatting requires a text field');
+ }
+
+ if (is_string($data)) {
+ $this->data = json_decode($data, true);
+ } else if (is_object($data)) {
+ $this->data = json_decode(json_encode($data), true);
+ } else if (is_array($data)) {
+ $this->data = $data;
+ }
+
+ if (is_null($this->data)) {
+ throw new PlDBBadValueException($data, $field, 'cannot interpret data as json: ' . $data);
+ }
+ }
+
+ public function format()
+ {
+ return XDB::escape(json_encode($this->data));
+ }
+
+ public function export()
+ {
+ return $this->data;
+ }
+
+ public function offsetExists($offset)
+ {
+ return isset($this->data[$offset]);
+ }
+
+ public function offsetGet($offset)
+ {
+ return $this->data[$offset];
+ }
+
+ public function offsetSet($offset, $value)
+ {
+ $this->data[$offset] = $value;
+ }
+
+ public function offsetUnset($offset)
+ {
+ unset($this->data[$offset]);
+ }
}
return $this->field($field)->format($value);
}
+ public function registerFieldFormatter($field, $class)
+ {
+ return $this->field($field)->registerFormatter($class);
+ }
+
+ public function registerFieldValidator($field, $class)
+ {
+ return $this->field($field)->registerValidator($class);
+ }
+
+
public function defaultValue($field)
{
return $this->field($field)->defaultValue;
$allowIncomplete));
}
+ public function exportEntry(PlDBTableEntry $entry)
+ {
+ $export = array();
+ foreach ($this->schema as $key=>$field) {
+ $value = $entry->$key;
+ if ($value instanceof PlExportable) {
+ $value = $value->export();
+ }
+ $export[$key] = $value;
+ }
+ return $export;
+ }
+
public static function get($name)
{
return new PlDBTable($name);
}
}
-class PlDBTableEntry extends PlAbstractIterable
+class PlDBTableEntry extends PlAbstractIterable implements PlExportable
{
private $table;
private $changed;
$this->changed = new PlFlagSet();
}
+ /** Register a custom formatter for a field.
+ *
+ * A formatter can be used to perform on-the-fly conversion from db storage to a user-friendly format.
+ * For example, if you have a textual field that contain json, you can use a JSonFieldFormatter on this
+ * field to perform automatic decoding when reading from the database (or when assigning the field)
+ * and automatic json_encoding when storing the object back to the db.
+ */
+ protected function registerFieldFormatter($field, $formatterClass)
+ {
+ $this->table->registerFieldFormatter($field, $formatterClass);
+ }
+
+ /** Register a custom validator for a field.
+ *
+ * A validator perform a pre-filter on the value of a field. As opposed to the formatters, it does
+ * not affects how the value is stored in the database.
+ */
+ protected function registerFieldValidator($field, $validatorClass)
+ {
+ $this->table->registerFieldValidator($field, $validatorClass);
+ }
+
/** This hook is called when the entry is going to be updated in the db.
*
* A typical usecase is a class that stores low-level representation of
public function copy(PlDBTableEntry $other)
{
Platal::assert($this->table == $other->table,
- "Trying to fill an entry of table {$this->table} with content of {$other->table}.");
+ "Trying to fill an entry of table {$this->table->table} with content of {$other->table->table}.");
$this->changed = $other->changed;
$this->fetched = $other->fetched;
$this->data = $other->data;
}
return $this->table->deleteEntry($this, true);
}
+
+ public function export()
+ {
+ return $this->table->exportEntry($this);
+ }
}
// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: