Commit 8db50d60 authored by Carsten  Rose's avatar Carsten Rose
Browse files

#3385 / templateGroup: insert/update/delete non primary records

Manual.rst: update doc how to insert/update/delete non primary templateGroup records.
FormAction.php: removed $templateGroupIndex - solved implicit by defining a LIMIT on 'slaveId' . Implemented '%D' (one below %d). Implemented FE_SQL_HONOR_FORM_ELEMENTS - reduces unecassary SQL queries.
HelperFormElement.php: moved function 'explodeTemplateGroupElements()' to 'QuickFormQuery.php'
Database.php: remove call to explodeTemplateGroupElements() - not necessary at that place.
QuickFormQuery.php: fill STORE_RECORD during Formload - to read templateGroup records very early. Local copy of `getNativeFormElements()`, new `explodeTemplateGroupElements()`
parent b15a9f95
......@@ -1266,7 +1266,7 @@ Type: templateGroup
*TemplateGroups* will be used to create a series of grouped (by the given *templateGroup*) *FormElements*.
FormElements can be assigned to a *templateGroup*. These *templateGroup* will be rendered upto *n*-times. On 'form load'
*FormElements* can be assigned to a *templateGroup*. These *templateGroup* will be rendered upto *n*-times. On 'form load'
only a single (=first) copy of the *templateGroup* will be shown. Below the last copy of the *templateGroup* an 'add'-button is
shown. If the user click on it, an additional copy of the *templateGroup* is displayed. This can be repeated up to
*templateGroup.maxLength* times. Also, the user can 'remove' previously created copies by clicking on a remove button near
......@@ -1294,6 +1294,43 @@ The placeholder `%d` can also be used in the *FormElement.label*
Example of styling the Add/ Delete Button: :ref:`example_class_template_group`
Column: primary record
''''''''''''''''''''''
If the columns `<name>%d` are real columns on the primary table, saving and delete (=empty string) are done automatically.
E.g. if there are upto five elements `grade1, ..., grade5` and the user inputs only the first three, the remaining will be set
to an empty string.
Column: non primary record
''''''''''''''''''''''''''
Additional logic is required to load, update, insert and/or delete slave records.
Load
;;;;
On each native *FormElement* of the *templateGroup* define a SQL query in the *FormElement.value* field. The query has to
select **all** values of all possible existing copies of the *FormElement* - therefore the exclamation mark is necessary.
Also define the order. E.g. *FormElement.value*::
{{!SELECT street FROM Address WHERE personId={{id}} ORDER BY id}}
Insert / Update / Delete
;;;;;;;;;;;;;;;;;;;;;;;;
Add an *action* record, type='afterSave', and assign the record to the given *templateGroup*.
In the parameter field define: ::
slaveId = {{SELECT id FROM Address WHERE personId={{id}} ORDER BY id LIMIT %D,1}}
sqlHonorFormElements = city%d, street%d
sqlUpdate = {{UPDATE Address SET city='{{city%d:FE:alnumx:s}}', street='{{street%d:FE:alnumx:s}}' WHERE id={{slaveId}} LIMIT 1}}
sqlInsert = {{INSERT INTO Address (`personId`, `city`, `street`) VALUES ({{id}}, '{{city%d:FE:alnumx:s}}' , '{{street%d:FE:alnumx:s}}') }}
sqlDelete = {{DELETE FROM Address WHERE id={{slaveId}} LIMIT 1}}
The `slaveId` needs attention: the placeholder `%d` starts always at 1. The `LIMIT` directive starts at 0 - therefore
use `%D` instead of `%d`, cause `%D` is always one below `%d` - but can **only** be used on the action element.
Class: Native
-------------
......@@ -2109,11 +2146,19 @@ sqlBefore / sqlInsert / sqlUpdate / sqlDelete / sqlAfter
* *requiredList* - List of `native`-*FormElement*: only if all of those elements are filled, the current
`action`-*FormElement* will be processed.
* *sqlBefore*: always fired (before any *sqlInsert* or *sqlUpdate*)
* *sqlInsert*: fired if *slaveId* = `0` or *slaveId* = `''`.
* *sqlUpdate*: fired if *slaveId* > `0`:
* *sqlDelete*: always fired (after *sqlInsert* or *sqlUpdate*) - the definition, when this query is fired, might change in the future.
* *sqlBefore*: always fired (before any *sqlInsert*, *sqlUpdate*, ..)
* *sqlInsert*: fired if *slaveId* == `0` or *slaveId* == `''`.
* *sqlUpdate*: fired if *slaveId* > `0`.
* *sqlDelete*: fired if *slaveId* > `0`, after *sqlInsert* or *sqlUpdate*. Be carefull not to delete filled records!
Always add a check, if values given, not to delete the record! *sqlHonorFormElements* helps to skip such checks.
* *sqlAfter*: always fired (after *sqlInsert*, *sqlUpdate* or *sqlDelete*).
* *sqlHonorFormElements*: list of *FormElement* names (this parameter is optional).
* If one of the named *FormElements* is not empty:
* fire *sqlInsert* if *slaveId* == `0`,
* fire *sqlUpdate* if *slaveId* > `0`
* If all of the named *FormElements* are empty:
* fire *sqlDelete* if *slaveId* > `0`
Example
'''''''
......@@ -2131,14 +2176,46 @@ Situation 1: master.xId=slave.id (1:1)
* If the automatic update of the master record is not suitable, the action element should have no name or a name
which does not exist as a column of the master record. Define `slaveId={{SELECT id ...}}`
* Two *FormElements* `myStreet` and `myCity`:
* Without *sqlHonorFormElements*. Parameter: ::
sqlInsert = INSERT INTO address (`street`, `city`) VALUES ('{{myStreet:FE:alnumx:s}}', '{{myCity:FE:alnumx:s}}')
sqlUpdate = UPDATE address SET `street` = '{{myStreet:FE:alnumx:s}}', `city` = '{{myCity:FE:alnumx:s}}' WHERE id={{slaveId}} LIMIT 1
sqlDelete = DELETE FROM address WHERE id={{slaveId}} AND '{{myStreet:FE:alnumx:s}}'='' AND '{{myCity:FE:alnumx:s}}'='' LIMIT 1
* With *sqlHonorFormElements*. Parameter: ::
sqlHonorFormElements = myStreet, myCity
sqlInsert = INSERT INTO address (`street`, `city`) VALUES ('{{myStreet:FE:alnumx:s}}', '{{myCity:FE:alnumx:s}}')
sqlUpdate = UPDATE address SET `street` = '{{myStreet:FE:alnumx:s}}', `city` = '{{myCity:FE:alnumx:s}}' WHERE id={{slaveId}} LIMIT 1
sqlDelete = DELETE FROM address WHERE id={{slaveId}} LIMIT 1
Situation 2: master.id=slave.xId (1:n)
* Name the action element different to any columnname of the master record (or no name).
* Name the action element *different* to any columnname of the master record (or no name).
* Determine the slaveId: `slaveId={{SELECT id FROM slave WHERE slave.xxx={{...}} LIMIT 1}}`
* {{slaveId}} == 0 ? 'sqlInsert' will be fired.
* {{slaveId}} != 0 ? 'sqlUpdate' will be fired.
* Two *FormElements* `myStreet` and `myCity`. The `person` is the master record, `address` is the slave:
* Without *sqlHonorFormElements*. Parameter: ::
slaveId = SELECT id FROM address WHERE personId={{id}} ORDER BY id LIMIT 1
sqlInsert = INSERT INTO address (`personId`, `street`, `city`) VALUES ({{id}}, '{{myStreet:FE:alnumx:s}}', '{{myCity:FE:alnumx:s}}')
sqlUpdate = UPDATE address SET `street` = '{{myStreet:FE:alnumx:s}}', `city` = '{{myCity:FE:alnumx:s}}' WHERE id={{slaveId}} LIMIT 1
sqlDelete = DELETE FROM address WHERE id={{slaveId}} AND '{{myStreet:FE:alnumx:s}}'='' AND '{{myCity:FE:alnumx:s}}'='' LIMIT 1
* With *sqlHonorFormElements*. Parameter: ::
slaveId = SELECT id FROM address WHERE personId={{id}} ORDER BY id LIMIT 1
sqlHonorFormElements = myStreet, myCity
sqlInsert = INSERT INTO address (`personId`, `street`, `city`) VALUES ({{id}}, '{{myStreet:FE:alnumx:s}}', '{{myCity:FE:alnumx:s}}')
sqlUpdate = UPDATE address SET `street` = '{{myStreet:FE:alnumx:s}}', `city` = '{{myCity:FE:alnumx:s}}' WHERE id={{slaveId}} LIMIT 1
sqlDelete = DELETE FROM address WHERE id={{slaveId}} LIMIT 1
Type: sendmail
^^^^^^^^^^^^^^
......
......@@ -529,7 +529,7 @@ abstract class AbstractBuildForm {
$config = OnArray::getArrayItems($formElement, [FE_LDAP_SERVER, FE_LDAP_BASE_DN, FE_LDAP_SEARCH, FE_LDAP_ATTRIBUTES]);
$config = $this->evaluate->parseArray($config);
if($formElement[FE_LDAP_USE_BIND_CREDENTIALS]==1) {
if ($formElement[FE_LDAP_USE_BIND_CREDENTIALS] == 1) {
$config[SYSTEM_LDAP_1_RDN] = $this->store->getVar(SYSTEM_LDAP_1_RDN, STORE_SYSTEM);
$config[SYSTEM_LDAP_1_PASSWORD] = $this->store->getVar(SYSTEM_LDAP_1_PASSWORD, STORE_SYSTEM);
}
......@@ -937,7 +937,7 @@ abstract class AbstractBuildForm {
FE_TYPEAHEAD_LIMIT => $formElement[FE_TYPEAHEAD_LIMIT],
];
if($formElement[FE_LDAP_USE_BIND_CREDENTIALS]=='1') {
if ($formElement[FE_LDAP_USE_BIND_CREDENTIALS] == '1') {
$arr[SYSTEM_LDAP_1_RDN] = $this->store->getVar(SYSTEM_LDAP_1_RDN, STORE_SYSTEM);
$arr[SYSTEM_LDAP_1_PASSWORD] = $this->store->getVar(SYSTEM_LDAP_1_PASSWORD, STORE_SYSTEM);
}
......@@ -3025,7 +3025,8 @@ EOT;
}
$default = $this->store->getStore(STORE_TABLE_DEFAULT); // current defaults
// evaluate FE_VALUE on all templateGroup FormElements.
// evaluate FE_VALUE on all templateGroup FormElements: After this call, in case of non-primary FEs, FE_VALUE
// might contain an array of values for all copies of the current FE.
$maxForeignRecords = $this->templateGroupDoValue();
$lastFilled = 0; // Marker if there is at least one element per copy who is filled.
......@@ -3035,16 +3036,16 @@ EOT;
// Per copy, iterate over all templateGroup FormElements.
foreach ($this->feSpecNative as $fe) {
$columnName = str_replace(FE_TEMPLATE_GROUP_NAME_PATTERN, $ii, $fe[FE_NAME]);
$fe[FE_NAME] = str_replace(FE_TEMPLATE_GROUP_NAME_PATTERN, $ii, $fe[FE_NAME]);
$fe[FE_LABEL] = str_replace(FE_TEMPLATE_GROUP_NAME_PATTERN, $ii, $fe[FE_LABEL]);
$fe[FE_NOTE] = str_replace(FE_TEMPLATE_GROUP_NAME_PATTERN, $ii, $fe[FE_NOTE]);
$columnName = $fe[FE_NAME];
// Column of primary table?
if (isset($record[$columnName])) {
if ($record[$columnName] != $default[$columnName]) {
$lastFilled = max($ii, $lastFilled);
}
$fe[FE_NAME] = $columnName;
} else {
$lastFilled = max($maxForeignRecords, $lastFilled);
......@@ -3053,6 +3054,7 @@ EOT;
}
// $fe[FE_TEMPLATE_GROUP_CURRENT_IDX] = $ii;
}
$feSpecNativeCopy[$ii - 1][] = $fe; // Build array with current copy of templateGroup.
}
......@@ -3089,7 +3091,10 @@ EOT;
/**
* Evaluate for all FormElements of the current templateGroup the field FE_VALUE.
* If the specific FormElement is not a real column of the primary table, than the value is probably a '{{!SELECT ...'
* Statement, that one will be fired. Additional the maximum count of all select rows will be determined and returned.
* statement, that one will be fired.
* In case of an non-primary FE, the result array are the values for the copies of the specific FE.
*
* Additional the maximum count of all select rows will be determined and returned.
*
* @return int max number of records in FormElement[FE_VALUE] over all FormElements.
* @throws UserFormException
......
......@@ -644,6 +644,7 @@ const FE_SQL_BEFORE = 'sqlBefore'; // Action: Always fired
const FE_SQL_UPDATE = 'sqlUpdate'; // Action: Update Statement for slave record
const FE_SQL_INSERT = 'sqlInsert'; // Action: Insert Statement to create slave record.
const FE_SQL_DELETE = 'sqlDelete'; // Action: Delete Statement to delete unused slave record.
const FE_SQL_HONOR_FORM_ELEMENTS = 'sqlHonorFormElements'; // Action: Honor given list of FormElements for sqlInsert|Update|Delete
const FE_EDITOR_PREFIX = 'editor-'; // TinyMCE configuration settings.
const FE_SENDMAIL_TO = 'sendMailTo'; // Receiver email adresses. Separate multiple by comma.
const FE_SENDMAIL_CC = 'sendMailCc'; // CC Receiver email adresses. Separate multiple by comma.
......@@ -672,6 +673,7 @@ const FE_TEMPLATE_GROUP_REMOVE_TEXT = 'tgRemoveText';
const FE_TEMPLATE_GROUP_CLASS = 'tgClass';
const FE_TEMPLATE_GROUP_DEFAULT_MAX_LENGTH = 5;
const FE_TEMPLATE_GROUP_NAME_PATTERN = '%d';
const FE_TEMPLATE_GROUP_NAME_PATTERN_0 = '%D';
const FE_TEMPLATE_GROUP_CURRENT_IDX = 'tgCurentIndex';
const FE_BUTTON_CLASS = 'buttonClass';
const FE_LDAP_SERVER = F_LDAP_SERVER;
......
......@@ -650,9 +650,6 @@ class Database {
// Check for retype FormElements which have to duplicated.
$feSpecNative = HelperFormElement::duplicateRetypeElements($feSpecNative);
// Check for templateGroup Elements to explode them
$feSpecNative = HelperFormElement::explodeTemplateGroupElements($feSpecNative);
// Copy Attributes to FormElements
$feSpecNative = HelperFormElement::copyAttributesToFormElements($formSpec, $feSpecNative);
......
......@@ -226,7 +226,9 @@ class QuickFormQuery {
$fillStoreForm->process();
}
$formName = $this->loadFormSpecification($formMode, $foundInStore);
$recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_TYPO3 . STORE_CLIENT);
$formName = $this->loadFormSpecification($formMode, $recordId, $foundInStore);
if ($formName === false && $formMode !== FORM_DELETE) {
// No form found: do nothing
return '';
......@@ -247,7 +249,6 @@ class QuickFormQuery {
$this->formSpec[F_TABLE_NAME] = $table;
}
$recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_TYPO3 . STORE_CLIENT);
// For 'new' record always create a new Browser TAB-uniq (for this current form, nowhere else used) SIP.
// With such a Browser TAB-uniq SIP, multiple Browser TABs and following repeated NEWs are easily implemented.
if (!$sipFound || ($formMode == FORM_LOAD && $recordId == 0)) {
......@@ -369,13 +370,14 @@ class QuickFormQuery {
* Loaded 'native' FormElements are in $this->feSpecNative
*
* @param string $mode FORM_LOAD|FORM_SAVE|FORM_UPDATE
* @param int $recordId
* @param string $foundInStore
* @return bool|string if found the formName, else 'false'.
* @throws CodeException
* @throws DbException
* @throws UserFormException
*/
private function loadFormSpecification($mode, &$foundInStore = '') {
private function loadFormSpecification($mode, $recordId, &$foundInStore = '') {
// formName
if (false === ($formName = $this->getFormName($mode, $foundInStore))) {
......@@ -413,6 +415,9 @@ class QuickFormQuery {
$this->formSpec = $formSpec;
// this is needed for filling templateGroup records with their default values
$this->fillStoreWithRecord($this->formSpec[F_TABLE_NAME], $recordId, STORE_RECORD);
// Clear
$this->store->setVar(SYSTEM_FORM_ELEMENT, '', STORE_SYSTEM);
......@@ -431,7 +436,8 @@ class QuickFormQuery {
case FORM_SAVE:
case FORM_UPDATE:
// $this->feSpecNative = $this->db->getNativeFormElements(SQL_FORM_ELEMENT_ALL_CONTAINER, ['no', $this->formSpec["id"], 'native'], $this->formSpec);
$this->feSpecNative = $this->db->getNativeFormElements(SQL_FORM_ELEMENT_NATIVE_TG_COUNT, [$this->formSpec["id"]], $this->formSpec);
$this->feSpecNative = $this->getNativeFormElements(SQL_FORM_ELEMENT_NATIVE_TG_COUNT, [$this->formSpec["id"]], $this->formSpec);
break;
case FORM_DELETE:
......@@ -445,6 +451,79 @@ class QuickFormQuery {
return $formName;
}
/**
* Depending on $sql reads FormElements to a specific container or all. Preprocess all FormElements.
* This code is dirty: the nearly same function exists in class 'Database' - the difference is only 'explodeTemplateGroupElements()'.
*
* @param string $sql SQL_FORM_ELEMENT_SPECIFIC_CONTAINER | SQL_FORM_ELEMENT_ALL_CONTAINER
* @param array $param Parameter which matches the prepared statement in $sql
* @param array $formSpec Main FormSpec to copy generic parameter to FormElements
* @return array|int
* @throws \qfq\CodeException
* @throws \qfq\DbException
*/
public function getNativeFormElements($sql, array $param, $formSpec) {
$feSpecNative = $this->db->sql($sql, ROW_REGULAR, $param);
// Explode and Do $FormElement.parameter
HelperFormElement::explodeParameterInArrayElements($feSpecNative, FE_PARAMETER);
// Check for retype FormElements which have to duplicated.
$feSpecNative = HelperFormElement::duplicateRetypeElements($feSpecNative);
// Check for templateGroup Elements to explode them
$feSpecNative = $this->explodeTemplateGroupElements($feSpecNative);
// Copy Attributes to FormElements
$feSpecNative = HelperFormElement::copyAttributesToFormElements($formSpec, $feSpecNative);
return $feSpecNative;
}
/**
* Iterate over all FormElements in $elements. If a row has a column NAME_TG_COPIES, copy those elements NAME_TG_COPIES-times.
* Adjust FE_TEMPLATE_GROUP_NAME_PATTERN (='%d') with current count on column FE_NAME and FE_LABEL.
*
* This code is dirty: only to get JSON value, we have to initialize the STORE_RECORD (done earlier) to be capable to
* parse fe[FE_VALUE], which probably contains as string like '{{!SELECT value FROM table WHERE xId={{id}} ORDER BY id}}' -
* the {{id}} needs to be replaced by the current recordId (primary record).
*
* Attention: The resulting order of the FormElements, is not the same as on the Form during FormLoad!
*
* @param array $elements
* @return array
*/
private function explodeTemplateGroupElements(array $elements) {
$new = array();
// No FormElements or no NAME_TG_COPIES column: nothing to do, return.
if ($elements == array() || count($elements) == 0 || !isset($elements[0][NAME_TG_COPIES])) {
return $elements;
}
// Iterate over all
foreach ($elements as $row) {
if (isset($row[NAME_TG_COPIES]) && $row[NAME_TG_COPIES] > 0) {
$row[FE_VALUE] = $this->eval->parse($row[FE_VALUE]);
for ($ii = 1; $ii <= $row[NAME_TG_COPIES]; $ii++) {
$tmpRow = $row;
if (is_array($row[FE_VALUE])) {
$tmpRow[FE_VALUE] = ($ii <= count($row[FE_VALUE])) ? current($row[FE_VALUE][$ii - 1]) : '';
}
unset($tmpRow[NAME_TG_COPIES]);
$tmpRow[FE_NAME] = str_replace(FE_TEMPLATE_GROUP_NAME_PATTERN, $ii, $tmpRow[FE_NAME]);
$tmpRow[FE_LABEL] = str_replace(FE_TEMPLATE_GROUP_NAME_PATTERN, $ii, $tmpRow[FE_LABEL]);
$new[] = $tmpRow;
}
} else {
$new[] = $row;
}
}
return $new;
}
/**
* Get the formName from STORE_TYPO3 (bodytext), STORE_SIP or by STORE_CLIENT (URL).
*
......
......@@ -13,6 +13,7 @@ require_once(__DIR__ . '/../Database.php');
require_once(__DIR__ . '/../store/Store.php');
require_once(__DIR__ . '/../Evaluate.php');
require_once(__DIR__ . '/../report/Sendmail.php');
require_once(__DIR__ . '/../helper/HelperFormElement.php');
/**
* Class formAction
......@@ -65,7 +66,7 @@ class FormAction {
* @throws DbException
* @throws UserFormException
*/
public function elements($recordId, array $feSpecAction, $feTypeList, $templateGroupIndex = 0) {
public function elements($recordId, array $feSpecAction, $feTypeList) {
$flagModified = false;
......@@ -85,18 +86,20 @@ class FormAction {
// Process templateGroup action elements
if (isset($fe[FE_ID_CONTAINER]) && $fe[FE_ID_CONTAINER] > 0) {
// Get native 'templateGroup'-FE - to retrieve MAX_LENGTH
$feTemplateGroup = $this->db->sql(SQL_FORM_ELEMENT_TEMPLATE_GROUP_FE_ID, ROW_REGULAR, [$fe[FE_ID_CONTAINER]]);
if (count($feTemplateGroup) == 1) {
$fe[FE_ID_CONTAINER] = 0;
for ($ii = 1; $ii < $feTemplateGroup[0][FE_MAX_LENGTH]; $ii++) {
for ($ii = 1; $ii <= $feTemplateGroup[0][FE_MAX_LENGTH]; $ii++) {
$feNew = OnArray::arrayValueReplace($fe, FE_TEMPLATE_GROUP_NAME_PATTERN, $ii);
if ($this->elements($recordId, [$feNew], $feTypeList, $ii)) {
$feNew = OnArray::arrayValueReplace($feNew, FE_TEMPLATE_GROUP_NAME_PATTERN_0, $ii - 1);
if ($this->elements($recordId, [$feNew], $feTypeList)) {
$flagModified = true;
}
}
} else {
// At the moment 'action' elements have to point to a templateGroup - nothing else is defined. Break if there is somethin else
// At the moment 'action' elements have to point to a templateGroup - nothing else is defined. Break if there is something else
throw new UserFormException("Expect a 'templateGroup' record in FormElement.id=", $fe[FE_ID_CONTAINER], ERROR_RECORD_NOT_FOUND);
}
continue; // skip to next FormElement
......@@ -142,7 +145,7 @@ class FormAction {
$this->validate($fe);
$this->doSlave($fe, $recordId, $templateGroupIndex);
$this->doSlave($fe, $recordId);
$flagModified = true;
}
......@@ -267,26 +270,15 @@ class FormAction {
*
* @param array $fe
* @param int $recordId
* @param int $templateGroupIndex
* @return int
* @throws CodeException
* @throws DbException
* @throws UserFormException
*/
private function doSlave(array $fe, $recordId, $templateGroupIndex) {
private function doSlave(array $fe, $recordId) {
// Get the slaveId
$slaveId = $this->evaluate->parse($fe[FE_SLAVE_ID]);
if ($templateGroupIndex > 0) {
if (is_array($slaveId)) {
// Select the n'th id of the array
$slaveId = isset($slaveId[$templateGroupIndex]) ? $slaveId[$templateGroupIndex] : 0;
} else {
throw new UserFormException("Result not an arry. SQL Statement for 'slaveId' in TemplateGroup should return an array with all 'slaveId's.", ERROR_EXPECTED_ARRAY);
}
} elseif (is_array($slaveId)) {
throw new UserFormException("Result not a single value. SQL Statement for 'slaveId' in should a single value.", ERROR_UNEXPECTED_TYPE);
}
if ($slaveId === '' && $fe[FE_NAME] !== '') {
// if the current action element has the same name as a real master record column: take that value as an id
......@@ -303,24 +295,41 @@ class FormAction {
// If given: fire a sqlBefore query
$this->evaluate->parse($fe[FE_SQL_BEFORE]);
$doInsert = ($slaveId == 0);
$doUpdate = ($slaveId != 0);
$doDelete = ($slaveId != 0);
$flagHonor = isset($fe[FE_SQL_HONOR_FORM_ELEMENTS]) && $fe[FE_SQL_HONOR_FORM_ELEMENTS] != '';
if ($flagHonor) {
$filled = $this->checkFormElements($fe[FE_SQL_HONOR_FORM_ELEMENTS]);
$doInsert = $filled && $doInsert;
$doUpdate = $filled && $doUpdate;
$doDelete = !$filled && $doDelete;
}
// Fire slave query
if ($slaveId == 0) {
if ($doInsert) {
$slaveId = $this->evaluate->parse($fe[FE_SQL_INSERT]);
// Store the slaveId: might be used later
$this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);
} else {
}
if ($doUpdate) {
$this->evaluate->parse($fe[FE_SQL_UPDATE]);
}
// Fire a delete query
if ($doDelete) {
$this->evaluate->parse($fe[FE_SQL_DELETE]);
$slaveId = 0;
}
// Check if there is a column with the same name as the 'action'-FormElement.
if (false !== $this->store->getVar($fe[FE_NAME], STORE_RECORD)) {
// After an insert or update, propagate the (new) slave id to the master record.
$this->db->sql("UPDATE " . $this->primaryTableName . " SET " . $fe[FE_NAME] . " = $slaveId WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]);
}
// If given: fire a delete query
$this->evaluate->parse($fe[FE_SQL_DELETE]);
// If given: fire a sqlAfter query
$this->evaluate->parse($fe[FE_SQL_AFTER]);
......@@ -328,6 +337,24 @@ class FormAction {
return $slaveId;
}
/**
* Iterates over list of FormElement-names and check STORE_FORM if there is a corresponding value. If at least one
* of the give elements is non empty, return true. If all elements are empty, return false.
*
* @param string $listOfFormElementNames E.g.: 'city, street, number'
* @return bool true if at lease one of the named elements is non empty on STORE_FORM (use SANATIZE_ALLOW_ALL to perform the check)
*/
private function checkFormElements($listOfFormElementNames) {
$arr = explode(',', $listOfFormElementNames);
foreach ($arr as $key) {
$value = $this->store->getVar(trim($key), STORE_FORM . STORE_EMPTY, SANITIZE_ALLOW_ALL);
if ($value != '') {
return true;
}
}
return false;
}
/**
* Set all necessary keys - subsequent 'isset()' are not necessary anymore.
*
......
......@@ -143,40 +143,6 @@ class HelperFormElement {
return $arr;
}
/**
* Iterate over all FormElements in $elements. If a row has a column NAME_TG_COPIES, copy those elements NAME_TG_COPIES-times.
* Adjust FE_TEMPLATE_GROUP_NAME_PATTERN (='%d') with current count on column FE_NAME and FE_LABEL.
*
* The resulting order of the FormElements, is not the same as on the Form during FormLoad!
*
* @param array $elements
* @return array
*/
public static function explodeTemplateGroupElements(array $elements) {
$new = array();
// No FormElements or no NAME_TG_COPIES column: nothing to do, return.
if ($elements == array() || count($elements) == 0 || !isset($elements[0][NAME_TG_COPIES])) {
return $elements;
}
// Iterate over all
foreach ($elements as $row) {
if (isset($row[NAME_TG_COPIES]) && $row[NAME_TG_COPIES] > 0) {
for ($ii = 1; $ii <= $row[NAME_TG_COPIES]; $ii++) {
$tmpRow = $row;
unset($tmpRow[NAME_TG_COPIES]);
$tmpRow[FE_NAME] = str_replace(FE_TEMPLATE_GROUP_NAME_PATTERN, $ii, $tmpRow[FE_NAME]);
$tmpRow[FE_LABEL] = str_replace(FE_TEMPLATE_GROUP_NAME_PATTERN, $ii, $tmpRow[FE_LABEL]);
$new[] = $tmpRow;
}
} else {
$new[] = $row;
}
}
return $new;
}
/**
* Copy specific attributes defined on the form to all FormElements.
*
......
......@@ -251,7 +251,7 @@ class FormActionTest extends \AbstractDatabaseTest {
$feSpecAction[FE_TYPE] = FE_TYPE_AFTER_SAVE;
$feSpecAction[FE_SQL_INSERT] = "{{ INSERT INTO Address (city, personId) VALUES ('invalid', {{r}}) }} ";
$feSpecAction[FE_SQL_UPDATE] = "{{ UPDATE Address SET city='Uptown' WHERE id={{slaveId:V}} }} ";
$feSpecAction[FE_SQL_DELETE] = "{{ DELETE FROM Address WHERE personId={{r}} AND id=0 }} ";
// $feSpecAction[FE_SQL_DELETE] = "{{ DELETE FROM Address WHERE personId={{r}} AND id=0 }} ";
$result = $this->db->sql("TRUNCATE Address");
$result = $this->db->sql("INSERT INTO Address (city, personId) VALUES ('Downtown1', 1)");
......@@ -286,8 +286,9 @@ class FormActionTest extends \AbstractDatabaseTest {
$result = $this->db->sql("SELECT id, city, personId FROM Address WHERE personId=$masterId", ROW_IMPLODE_ALL);
$this->assertEquals('3Uptown' . $masterId, $result);
$result = $this->db->sql("SELECT id, name, adrId FROM Person WHERE id=$masterId", ROW_IMPLODE_ALL);
$this->assertEquals('2Smith3', $result);
$this->assertEquals('2Smith0', $result);
}
......
......@@ -160,32 +160,32 @@ class HelperFormElementTest extends \PHPUnit_Framework_TestCase {
$this->assertEquals($expect, $result, "Both arrays should be equal");
}
public function testExplodeTemplateGroupElements() {
$formElements = array();
$result = HelperFormElement::explodeTemplateGroupElements(array());
$this->assertEquals(array(), $result, "Both arrays should be equal");
$formElements = [[FE_NAME => 'nameA', FE_LABEL => 'labelA', FE_VALUE => '1'], [FE_NAME => 'nameB', FE_LABEL => 'labelB', FE_VALUE => '11']];
$result = HelperFormElement::explodeTemplateGroupElements($formElements);
$this->assertEquals($formElements, $result, "Both arrays should be equal");
$formElements = [[FE_NAME => 'nameA', FE_LABEL => 'label' . FE_TEMPLATE_GROUP_NAME_PATTERN . 'A', FE_VALUE => '1'], [FE_NAME => 'nameB', FE_LABEL => 'labelB', FE_VALUE => '11']];
$result = HelperFormElement::explodeTemplateGroupElements($formElements);
$this->assertEquals($formElements, $result, "Both arrays should be equal");
$formElements = [[FE_NAME => 'nameA', FE_LABEL => 'label' . FE_TEMPLATE_GROUP_NAME_PATTERN . 'A', FE_VALUE => '1', NAME_TG_COPIES => 2], [FE_NAME => 'nameB', FE_LABEL => 'labelB', FE_VALUE => '11']];
$result = HelperFormElement::explodeTemplateGroupElements($formElements);
$formElements = [[FE_NAME => 'nameA', FE_LABEL => 'label1A', FE_VALUE => '1'],
[FE_NAME => 'nameA', FE_LABEL => 'label2A', FE_VALUE => '1'],
[FE_NAME => 'nameB', FE_LABEL => 'labelB', FE_VALUE => '11']];
$this->assertEquals($formElements, $result, "Both arrays should be equal");
$formElements = [[FE_NAME => FE_TEMPLATE_GROUP_NAME_PATTERN . 'nameA', FE_LABEL => 'label' . FE_TEMPLATE_GROUP_NAME_PATTERN . 'A', FE_VALUE => '1', NAME_TG_COPIES => 2], [FE_NAME => 'nameB', FE_LABEL => 'labelB', FE_VALUE => '11']];
$result = HelperFormElement::explodeTemplateGroupElements($formElements);
$formElements = [[FE_NAME => '1nameA', FE_LABEL => 'label1A', FE_VALUE => '1'],
[FE_NAME => '2nameA', FE_LABEL => 'label2A', FE_VALUE => '1'],
[FE_NAME => 'nameB', FE_LABEL => 'labelB', FE_VALUE => '11']];
$this->assertEquals($formElements, $result, "Both arrays should be equal");
}
// public function testExplodeTemplateGroupElements() {
// $formElements = array();
//
// $result = HelperFormElement::explodeTemplateGroupElements(array());
// $this->assertEquals(array(), $result, "Both arrays should be equal");
//
// $formElements = [[FE_NAME => 'nameA', FE_LABEL => 'labelA', FE_VALUE => '1'], [FE_NAME => 'nameB', FE_LABEL => 'labelB', FE_VALUE => '11']];
// $result = HelperFormElement::explodeTemplateGroupElements($formElements);
// $this->assertEquals($formElements, $result, "Both arrays should be equal");
//
// $formElements = [[FE_NAME => 'nameA', FE_LABEL => 'label' . FE_TEMPLATE_GROUP_NAME_PATTERN . 'A', FE_VALUE => '1'], [FE_NAME => 'nameB', FE_LABEL => 'labelB', FE_VALUE => '11']];