Do not add empty job address in vcard.
[platal.git] / classes / pldbtableentry.php
index 8d9c038..1ab5d4c 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /***************************************************************************
- *  Copyright (C) 2003-2010 Polytechnique.org                              *
+ *  Copyright (C) 2003-2011 Polytechnique.org                              *
  *  http://opensource.polytechnique.org/                                   *
  *                                                                         *
  *  This program is free software; you can redistribute it and/or modify   *
@@ -74,6 +74,7 @@ class PlDBTableField
     public $defaultValue;
     public $autoIncrement;
 
+    private $validator;
     private $formatter;
 
     public function __construct(array $column)
@@ -104,6 +105,11 @@ class PlDBTableField
         $this->formatter = $class;
     }
 
+    public function registerValidator($class)
+    {
+        $this->validator = $class;
+    }
+
     public function format($value, $badNullFallbackToDefault = false)
     {
         if (is_null($value)) {
@@ -114,7 +120,12 @@ class PlDBTableField
                 return $this->defaultValue;
             }
             throw new PlDBBadValueException($value, $this, 'null not allowed');
-        } else if (!is_null($this->formatter)) {
+        }
+        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') {
@@ -154,11 +165,15 @@ class PlDBTableField
     }
 }
 
-interface PlDBTableFieldFormatter extends XDBFormat
+interface PlDBTableFieldValidator
 {
     public function __construct(PlDBTableField $field, $value);
 }
 
+interface PlDBTableFieldFormatter extends PlDBTableFieldValidator, XDBFormat, PlExportable
+{
+}
+
 class DateFieldFormatter implements PlDBTableFieldFormatter
 {
     private $datetime;
@@ -181,13 +196,18 @@ class DateFieldFormatter implements PlDBTableFieldFormatter
 
     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
@@ -203,7 +223,11 @@ class JSonFieldFormatter implements PlDBTableFieldFormatter, ArrayAccess
         if (is_string($data)) {
             $this->data = json_decode($data, true);
         } else if (is_object($data)) {
-            $this->data = json_decode(json_encode($data), true);
+            if ($data instanceof PlExportable) {
+                $this->data = $data->export();
+            } else {
+                $this->data = json_decode(json_encode($data), true);
+            }
         } else if (is_array($data)) {
             $this->data = $data;
         }
@@ -218,6 +242,11 @@ class JSonFieldFormatter implements PlDBTableFieldFormatter, ArrayAccess
         return XDB::escape(json_encode($this->data));
     }
 
+    public function export()
+    {
+        return $this->data;
+    }
+
     public function offsetExists($offset)
     {
         return isset($this->data[$offset]);
@@ -325,6 +354,12 @@ class PlDBTable
         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;
@@ -446,34 +481,46 @@ class PlDBTable
     const SAVE_INSERT_MISSING   = 0x01;
     const SAVE_UPDATE_EXISTING  = 0x02;
     const SAVE_IGNORE_DUPLICATE = 0x04;
-    public function saveEntry(PlDBTableEntry $entry, $flags)
+    public function saveEntries(array $entries, $flags)
     {
         $flags &= (self::SAVE_INSERT_MISSING | self::SAVE_UPDATE_EXISTING | self::SAVE_IGNORE_DUPLICATE);
         Platal::assert($flags != 0, "Hey, the flags ($flags) here are so stupid, don't know what to do");
         if ($flags == self::SAVE_UPDATE_EXISTING) {
-            $values = array();
-            foreach ($this->mutableFields as $field) {
-                if ($entry->hasChanged($field)) {
-                    $values[] = XDB::format($field . ' = {?}', $entry->$field);
+            foreach ($entries as $entry) {
+                $values = array();
+                foreach ($this->mutableFields as $field) {
+                    if ($entry->hasChanged($field)) {
+                        $values[] = XDB::format($field . ' = {?}', $entry->$field);
+                    }
+                }
+                if (count($values) > 0) {
+                    XDB::rawExecute('UPDATE ' . $this->table . '
+                                        SET ' . implode(', ', $values) . '
+                                      WHERE ' . $this->buildKeyCondition($entry,
+                                                                         $this->keyFields(self::PRIMARY_KEY),
+                                                                         false));
                 }
-            }
-            if (count($values) > 0) {
-                XDB::rawExecute('UPDATE ' . $this->table . '
-                                    SET ' . implode(', ', $values) . '
-                                  WHERE ' . $this->buildKeyCondition($entry,
-                                                                     $this->keyFields(self::PRIMARY_KEY),
-                                                                     false));
             }
         } else {
-            $values = array();
-            foreach ($this->schema as $field=>$type) {
-                if ($entry->hasChanged($field)) {
-                    $values[$field] = XDB::escape($entry->$field);
+            $fields = new PlFlagSet();
+            foreach ($entries as $entry) {
+                foreach ($this->schema as $field=>$type) {
+                    if ($type->inPrimaryKey || $entry->hasChanged($field)) {
+                        $fields->addFlag($field);
+                    }
                 }
             }
-            if (count($values) > 0) {
-                $query = $this->table . ' (' . implode(', ', array_keys($values)) . ')
-                               VALUES  (' . implode(', ', $values) . ')';
+            if (count($fields->export()) > 0) {
+                foreach ($entries as $entry) {
+                    $v = array();
+                    foreach ($fields as $field) {
+                        $v[$field] = XDB::escape($entry->$field);
+                    }
+                    $values[] = '(' . implode(', ', $v) . ')';
+                }
+
+                $query = $this->table . ' (' . implode(', ', $fields->export()) . ')
+                               VALUES ' . implode(",\n", $values);
                 if (($flags & self::SAVE_UPDATE_EXISTING)) {
                     $update = array();
                     foreach ($this->mutableFields as $field) {
@@ -482,23 +529,26 @@ class PlDBTable
                         }
                     }
                     if (count($update) > 0) {
-                        $query = 'INSERT ' . $query;
+                        $query = 'INSERT INTO ' . $query;
                         $query .= "\n  ON DUPLICATE KEY UPDATE " . implode(', ', $update);
                     } else {
-                        $query = 'INSERT IGNORE ' . $query;
+                        $query = 'INSERT IGNORE INTO ' . $query;
                     }
                 } else if (($flags & self::SAVE_IGNORE_DUPLICATE)) {
-                    $query = 'INSERT IGNORE ' . $query;
+                    $query = 'INSERT IGNORE INTO ' . $query;
                 } else {
-                    $query = 'INSERT ' . $query;
+                    $query = 'INSERT INTO ' . $query;
                 }
                 XDB::rawExecute($query);
-                $id = XDB::insertId();
-                if ($id) {
-                    foreach ($this->primaryKey as $field) {
-                        if ($this->schema[$field]->autoIncrement) {
-                            $entry->$field = $id;
-                            break;
+                if (count($entries) == 1) {
+                    $id = XDB::insertId();
+                    if ($id) {
+                        $entry = end($entries);
+                        foreach ($this->primaryKey as $field) {
+                            if ($this->schema[$field]->autoIncrement) {
+                                $entry->$field = $id;
+                                break;
+                            }
                         }
                     }
                 }
@@ -514,13 +564,26 @@ class PlDBTable
                                                                   $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;
@@ -552,6 +615,16 @@ class PlDBTableEntry extends PlAbstractIterable
         $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
@@ -679,13 +752,7 @@ class PlDBTableEntry extends PlAbstractIterable
 
     public function save($flags)
     {
-        if (!$this->preSave()) {
-            return false;
-        }
-        $this->table->saveEntry($this, $flags);
-        $this->changed->clear();
-        $this->postSave();
-        return true;
+        return self::saveBatch(array($this), $flags);
     }
 
     public function update($insertMissing = false)
@@ -713,6 +780,41 @@ class PlDBTableEntry extends PlAbstractIterable
         }
         return $this->table->deleteEntry($this, true);
     }
+
+    public function export()
+    {
+        return $this->table->exportEntry($this);
+    }
+
+    protected static function saveBatch($entries, $flags)
+    {
+        $table = null;
+        foreach ($entries as $entry) {
+            if (is_null($table)) {
+                $table = $entry->table;
+            } else {
+                Platal::assert($table === $entry->table, "Cannot save batch of entries of different kinds");
+            }
+            if (!$entry->preSave()) {
+                return false;
+            }
+        }
+        $table->saveEntries($entries, $flags);
+        foreach ($entries as $entry) {
+            $entry->changed->clear();
+            $entry->postSave();
+        }
+        return true;
+    }
+
+    public static function insertBatch($entries, $allowUpdate = false)
+    {
+        $flags = PlDBTable::SAVE_INSERT_MISSING;
+        if ($allowUpdate) {
+            $flags |= PlDBTable::SAVE_UPDATE_EXISTING;
+        }
+        return self::saveBatch($entries, $flags);
+    }
 }
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: