From 092e6deb58257565982fd8f3d1cbd175d7a0abe0 Mon Sep 17 00:00:00 2001
From: Carsten  Rose <carsten.rose@math.uzh.ch>
Date: Wed, 13 Apr 2016 16:34:31 +0200
Subject: [PATCH] Preparation to update Form after saving values. save.php:
 return JSON with updated values of all fields. AbstractBuildForm.php: updated
 to deliver values of all form elements Constants.php: constants to
 distinguish all or specific values of FEs. QUERY_TYPE_* Database.php: doSQL
 now returns affectedrows or lastinsertid in case of udpate/inset. Typos
 fixed. QuickFormQuery.php: deliver Values as JSON after save. Save.php:
 return lastinsertid or affectedrows.

---
 extension/qfq/api/save.php              |  4 +-
 extension/qfq/qfq/AbstractBuildForm.php |  9 +--
 extension/qfq/qfq/Constants.php         | 16 ++++-
 extension/qfq/qfq/Database.php          | 95 ++++++++++++++-----------
 extension/qfq/qfq/QuickFormQuery.php    | 36 +++++-----
 extension/qfq/qfq/Save.php              | 22 +++---
 6 files changed, 108 insertions(+), 74 deletions(-)

diff --git a/extension/qfq/api/save.php b/extension/qfq/api/save.php
index f58dc9270..8dafb8f64 100644
--- a/extension/qfq/api/save.php
+++ b/extension/qfq/api/save.php
@@ -58,11 +58,13 @@ $answer[API_MESSAGE] = '';
 try {
     $qfq = new \qfq\QuickFormQuery(['bodytext' => ""]);
 
-    $qfq->saveForm();
+    $data = $qfq->saveForm();
 
     $answer[API_REDIRECT] = $qfq->getForwardMode($answer[API_REDIRECT_URL]);
     $answer[API_STATUS] = API_ANSWER_STATUS_SUCCESS;
     $answer[API_MESSAGE] = 'save: success';
+    $answer[API_FORM_UPDATE] = $data;
+
 
 } catch (qfq\UserFormException $e) {
     $answer[API_MESSAGE] = $e->formatMessage();
diff --git a/extension/qfq/qfq/AbstractBuildForm.php b/extension/qfq/qfq/AbstractBuildForm.php
index f468ab779..43990df36 100644
--- a/extension/qfq/qfq/AbstractBuildForm.php
+++ b/extension/qfq/qfq/AbstractBuildForm.php
@@ -124,6 +124,7 @@ abstract class AbstractBuildForm {
         $htmlSubrecords = '';
         $htmlElements = '';
         $json = array();
+        $modeCollectFe = ($mode === FORM_SAVE) ? FLAG_ALL : FLAG_DYNAMIC_UPDATE;
 
         // <form>
         if ($mode === FORM_LOAD) {
@@ -140,11 +141,11 @@ abstract class AbstractBuildForm {
             foreach ($parentRecords as $row) {
                 $this->store->setVarArray($row, STORE_PARENT_RECORD, true);
                 $jsonTmp = array();
-                $htmlElements = $this->elements($row['_id'], $filter, 0, $jsonTmp);
+                $htmlElements = $this->elements($row['_id'], $filter, 0, $jsonTmp, $modeCollectFe);
                 $json[] = $jsonTmp;
             }
         } else {
-            $htmlElements = $this->elements($this->store->getVar(SIP_RECORD_ID, STORE_SIP), $filter, 0, $json);
+            $htmlElements = $this->elements($this->store->getVar(SIP_RECORD_ID, STORE_SIP), $filter, 0, $json, $modeCollectFe);
         }
 
         // </form>
@@ -299,7 +300,7 @@ abstract class AbstractBuildForm {
      * @throws DbException
      * @throws \qfq\UserFormException
      */
-    public function elements($recordId, $filter = FORM_ELEMENTS_NATIVE, $feIdContainer = 0, &$json) {
+    public function elements($recordId, $filter = FORM_ELEMENTS_NATIVE, $feIdContainer = 0, &$json, $modeCollectFe = FLAG_DYNAMIC_UPDATE) {
         $html = '';
 
         // get current data record
@@ -348,7 +349,7 @@ abstract class AbstractBuildForm {
                 }
             } else {
                 // for non container elements: just add the current json status
-                if ($fe['dynamicUpdate'] == 'yes') {
+                if ($modeCollectFe === FLAG_ALL || ($modeCollectFe == FLAG_DYNAMIC_UPDATE && $fe['dynamicUpdate'] == 'yes')) {
                     $json[] = $jsonElement;
                 }
             }
diff --git a/extension/qfq/qfq/Constants.php b/extension/qfq/qfq/Constants.php
index 04202e5c7..339fa0e67 100644
--- a/extension/qfq/qfq/Constants.php
+++ b/extension/qfq/qfq/Constants.php
@@ -161,8 +161,9 @@ const ERROR_DB_COUNT_DO_NOT_MATCH = 2008;
 const ERROR_DB_CLOSE_MYSQLI_RESULT = 2009;
 const ERROR_DB_CLOSE_MYSQLI_STMT = 2010;
 const ERROR_DB_UNKNOWN_COLUMN = 2011;
-const ERROR_DB_MISSING_COLUMN_ID = 2012;
-const ERROR_DB_COLUMN_NOT_FOUND_IN_TABLE = 2013;
+const ERROR_DB_UNKNOWN_COMMAND = 2012;
+const ERROR_DB_MISSING_COLUMN_ID = 2013;
+const ERROR_DB_COLUMN_NOT_FOUND_IN_TABLE = 2014;
 //
 // Store Names: Identifier
 //
@@ -333,4 +334,13 @@ const GLYPH_ICON_CHECK = 'glyphicon glyphicon-ok';
 
 // SUPPORT
 const PARAM_T3_ALL = 't3 all';
-const PARAM_T3_NO_ID = "t3 no id";
\ No newline at end of file
+const PARAM_T3_NO_ID = "t3 no id";
+
+// AbstractBuildForm
+const FLAG_ALL = 'flagAll';
+const FLAG_DYNAMIC_UPDATE = 'flagDynamicUpdate';
+
+
+const QUERY_TYPE_SELECT = 'type: select,show,describe,explain';
+const QUERY_TYPE_INSERT = 'type: insert';
+const QUERY_TYPE_UPDATE = 'type: update,replace,delete';
\ No newline at end of file
diff --git a/extension/qfq/qfq/Database.php b/extension/qfq/qfq/Database.php
index 0ab2685ca..823fd5a82 100644
--- a/extension/qfq/qfq/Database.php
+++ b/extension/qfq/qfq/Database.php
@@ -201,11 +201,15 @@ class Database {
      * @param string $mode
      * @param array $parameterArray
      * @param string $specificMessage
-     * @return mixed|null                  If no record found, empty string ( ROW_EXPECT_0_1, ROW_EXPECT_1) or empty array (all other modes)
+     * @return mixed|null
+     *              SELECT | SHOW | DESCRIBE | EXPLAIN: If no record found, empty string ( ROW_EXPECT_0_1, ROW_EXPECT_1) or empty array (all other modes)
+     *              INSERT: last_insert_id
+     *              UPDATE | DELETE | REPLACE: affected rows
      * @throws \qfq\CodeException
      * @throws \qfq\DbException
      */
     public function sql($sql, $mode = ROW_REGULAR, array $parameterArray = array(), $specificMessage = '', array &$keys = array()) {
+        $queryType = '';
         $result = array();
         $this->closeMysqliStmt();
 
@@ -217,48 +221,52 @@ class Database {
         if ($specificMessage)
             $specificMessage .= " ";
 
-        $count = $this->prepareExecute($sql, $parameterArray);
+        $count = $this->prepareExecute($sql, $parameterArray, $queryType);
         if ($count === false) {
             throw new DbException($specificMessage . "No idea why this error happens - please take some time and check this: $sql", ERROR_DB_GENERIC_CHECK);
         }
 
-        switch ($mode) {
-            case ROW_IMPLODE_ALL:
-                $result = $this->fetchAll($mode);
-                break;
-            case ROW_KEYS:
-            case ROW_REGULAR:
-                $result = $this->fetchAll($mode, $keys);
-                break;
-            case ROW_EXPECT_0:
-                if ($count === 0)
-                    $result = array();
-                else
-                    throw new DbException($specificMessage . "Expected no record, got $count rows: $sql", ERROR_DB_TOO_MANY_ROWS);
-                break;
-            case ROW_EXPECT_1:
-                if ($count === 1)
-                    $result = $this->fetchAll($mode)[0];
-                else
-                    throw new DbException($specificMessage . "Expected one record, got $count: $sql", ERROR_DB_COUNT_DO_NOT_MATCH);
-                break;
-            case ROW_EXPECT_0_1:
-                if ($count === 1)
-                    $result = $this->fetchAll($mode)[0];
-                elseif ($count === 0)
-                    $result = array();
-                else
-                    throw new DbException($specificMessage . "Expected no record, got $count rows: $sql", ERROR_DB_TOO_MANY_ROWS);
-                break;
-            case ROW_EXPECT_GE_1:
-                if ($count > 0)
+        if ($queryType === QUERY_TYPE_SELECT) {
+            switch ($mode) {
+                case ROW_IMPLODE_ALL:
                     $result = $this->fetchAll($mode);
-                else
-                    throw new DbException($specificMessage . "Expected at least one record, got nothing: $sql", ERROR_DB_TOO_FEW_ROWS);
-                break;
+                    break;
+                case ROW_KEYS:
+                case ROW_REGULAR:
+                    $result = $this->fetchAll($mode, $keys);
+                    break;
+                case ROW_EXPECT_0:
+                    if ($count === 0)
+                        $result = array();
+                    else
+                        throw new DbException($specificMessage . "Expected no record, got $count rows: $sql", ERROR_DB_TOO_MANY_ROWS);
+                    break;
+                case ROW_EXPECT_1:
+                    if ($count === 1)
+                        $result = $this->fetchAll($mode)[0];
+                    else
+                        throw new DbException($specificMessage . "Expected one record, got $count: $sql", ERROR_DB_COUNT_DO_NOT_MATCH);
+                    break;
+                case ROW_EXPECT_0_1:
+                    if ($count === 1)
+                        $result = $this->fetchAll($mode)[0];
+                    elseif ($count === 0)
+                        $result = array();
+                    else
+                        throw new DbException($specificMessage . "Expected no record, got $count rows: $sql", ERROR_DB_TOO_MANY_ROWS);
+                    break;
+                case ROW_EXPECT_GE_1:
+                    if ($count > 0)
+                        $result = $this->fetchAll($mode);
+                    else
+                        throw new DbException($specificMessage . "Expected at least one record, got nothing: $sql", ERROR_DB_TOO_FEW_ROWS);
+                    break;
 
-            default:
-                throw new DbException($specificMessage . "Unknown mode: $mode", ERROR_UNKNOWN_MODE);
+                default:
+                    throw new DbException($specificMessage . "Unknown mode: $mode", ERROR_UNKNOWN_MODE);
+            }
+        } else {
+            $result = $count;
         }
 
         $this->closeMysqliStmt();
@@ -293,12 +301,13 @@ class Database {
      *
      * @param string $sql SQL statement with prepared statement variable.
      * @param array $parameterArray parameter array for prepared statement execution.
+     * @param string $queryType returns QUERY_TYPE_SELECT | QUERY_TYPE_UPDATE | QUERY_TYPE_INSERT, depending on the query.
      * @return int|mixed
      * @throws \qfq\CodeException
      * @throws \qfq\DbException
      * @throws \qfq\UserFormException
      */
-    private function prepareExecute($sql, array $parameterArray = array()) {
+    private function prepareExecute($sql, array $parameterArray = array(), &$queryType = '') {
         $result = 0;
         $this->store->setVar(SYSTEM_SQL_FINAL, $sql, STORE_SYSTEM);
         $this->store->setVar(SYSTEM_SQL_PARAM_ARRAY, $parameterArray, STORE_SYSTEM);
@@ -332,21 +341,25 @@ class Database {
                 if (false === ($result = $this->mysqli_stmt->get_result())) {
                     throw new DbException('[ mysqli: ' . $this->mysqli_stmt->errno . ' ] ' . $this->mysqli_stmt->error, ERROR_DB_EXECUTE);
                 }
+            $queryType = QUERY_TYPE_SELECT;
                 $this->mysqli_result = $result;
                 $count = $this->mysqli_result->num_rows;
             $msg = 'Read rows: ' . $count;
                 break;
             case 'INSERT':
+                $queryType = QUERY_TYPE_INSERT;
                 $count = $this->mysqli->insert_id;
                 $msg = 'ID: ' . $count;
                 break;
             case 'UPDATE':
             case 'REPLACE':
             case 'DELETE':
+            $queryType = QUERY_TYPE_UPDATE;
                 $count = $this->mysqli->affected_rows;
                 $msg = 'Affected rows: ' . $count;
                 break;
             default:
+                throw new DbException('Unknown comand: "' . $command . '"', ERROR_DB_UNKNOWN_COMMAND);
                 break;
         }
 
@@ -359,7 +372,7 @@ class Database {
     }
 
     /**
-     * Decide if the SQL statement has to be logged.If yes, create a timestamp and do the log.
+     * Decide if the SQL statement has to be logged. If yes, create a timestamp and do the log.
      *
      * @param $sql
      * @param array $parameterArray
@@ -447,12 +460,12 @@ class Database {
      *
      * mode:
      *  ROW_IMPLODE_ALL: Return string. All cells of all rows imploded to one string.
-     *  ROW_KEYS: Retrun num array with column names in $keys
+     *  ROW_KEYS: Return num array with column names in $keys
      *  default: Return 2-dimensional assoc array
      *
      * @param string $mode
      * @param array $keys
-     * @return array|bool|mixed|string false in case of error.
+     * @return array|bool|mixed|string false in case of an error.
      *              Empty string is returned if the query didn't yield any rows.
      *              All rows as Multi Assoc array if $mode!=IMPLODE_ALL.
      *              All rows and all columns imploded to one string if $mode=IMPLODE_ALL
diff --git a/extension/qfq/qfq/QuickFormQuery.php b/extension/qfq/qfq/QuickFormQuery.php
index a40638a6f..399c3892d 100644
--- a/extension/qfq/qfq/QuickFormQuery.php
+++ b/extension/qfq/qfq/QuickFormQuery.php
@@ -178,7 +178,7 @@ class QuickFormQuery {
      * $mode=FORM_UPDATE: States and values of all form elements will be returned as JSON.
      * $mode=FORM_SAVE: The submitted form will be saved. Return Failure or Success as JSON.
      *
-     * @param $mode   FORM_LOAD | FORM_UPDATE | FORM_SAVE
+     * @param string $mode FORM_LOAD | FORM_UPDATE | FORM_SAVE
      * @return array|string
      * @throws CodeException
      * @throws UserFormException
@@ -203,22 +203,23 @@ class QuickFormQuery {
         }
         $this->store->fillStoreTableDefaultColumnType($this->formSpec['tableName']);
 
+        switch ($this->formSpec['render']) {
+            case 'plain':
+                $build = new BuildFormPlain($this->formSpec, $this->feSpecAction, $this->feSpecNative);
+                break;
+            case 'table':
+                $build = new BuildFormTable($this->formSpec, $this->feSpecAction, $this->feSpecNative);
+                break;
+            case 'bootstrap':
+                $build = new BuildFormBootstrap($this->formSpec, $this->feSpecAction, $this->feSpecNative);
+                break;
+            default:
+                throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
+        }
+
         switch ($mode) {
             case FORM_LOAD:
             case FORM_UPDATE:
-                switch ($this->formSpec['render']) {
-                    case 'plain':
-                        $build = new BuildFormPlain($this->formSpec, $this->feSpecAction, $this->feSpecNative);
-                        break;
-                    case 'table':
-                        $build = new BuildFormTable($this->formSpec, $this->feSpecAction, $this->feSpecNative);
-                        break;
-                    case 'bootstrap':
-                        $build = new BuildFormBootstrap($this->formSpec, $this->feSpecAction, $this->feSpecNative);
-                        break;
-                    default:
-                        throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
-                }
 
                 $data = $build->process($mode);
                 break;
@@ -226,6 +227,9 @@ class QuickFormQuery {
             case FORM_SAVE:
                 $save = new Save($this->formSpec, $this->feSpecAction, $this->feSpecNative);
                 $save->process();
+
+                // Retrieve FE Values as JSON
+                $data = $build->process($mode);
                 break;
 
             default:
@@ -437,9 +441,9 @@ class QuickFormQuery {
      */
     public function saveForm() {
 
-        $html = $this->doForm(FORM_SAVE);
+        $json = $this->doForm(FORM_SAVE);
 
-        return $html;
+        return $json;
     }
 
     /**
diff --git a/extension/qfq/qfq/Save.php b/extension/qfq/qfq/Save.php
index bcd8394b4..38a261fdf 100644
--- a/extension/qfq/qfq/Save.php
+++ b/extension/qfq/qfq/Save.php
@@ -125,7 +125,7 @@ class Save {
     public function insertRecord($tableName, array $values) {
 
         if (count($values) === 0)
-            return 0;
+            return 0; // nothing to write, last insert id=0
 
         $paramList = str_repeat('?, ', count($values));
         $paramList = substr($paramList, 0, strlen($paramList) - 2);
@@ -133,27 +133,29 @@ class Save {
 
         $sql = 'INSERT INTO ' . $tableName . ' ( ' . $columnList . ' ) VALUES ( ' . $paramList . ' )';
 
-        $this->db->sql($sql, ROW_REGULAR, array_values($values));
+        $rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));
 
-        return $this->db->getLastInsertId();
+        return $rc;
     }
 
     /**
+     * @param string $tableName
      * @param array $values
-     * @param $recordId
-     * @return string
+     * @param int $recordId
+     * @return bool|int     false if $values is empty
+     * @throws CodeException
      * @throws DbException
      */
     public function updateRecord($tableName, array $values, $recordId) {
 
         if (count($values) === 0)
-            return;
+            return 0; // nothing to write, 0 rows affected
 
         if ($recordId === 0)
             throw new CodeException('RecordId=0 - this is not possible for update.', ERROR_RECORDID_0_FORBIDDEN);
 
-        $paramList = str_repeat('?, ', count($values));
-        $paramList = substr($paramList, 0, strlen($paramList) - 2);
+//        $paramList = str_repeat('?, ', count($values));
+//        $paramList = substr($paramList, 0, strlen($paramList) - 2);
 
         $sql = 'UPDATE `' . $tableName . '` SET ';
 
@@ -165,7 +167,9 @@ class Save {
         $sql = substr($sql, 0, strlen($sql) - 2) . ' WHERE id = ?';
         $values[] = $recordId;
 
-        $this->db->sql($sql, ROW_REGULAR, array_values($values));
+        $rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));
+
+        return $rc;
     }
 
 }
\ No newline at end of file
-- 
GitLab