FormAction.php 10.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 5/29/16
 * Time: 5:24 PM
 */

namespace qfq;

require_once(__DIR__ . '/../Constants.php');
require_once(__DIR__ . '/../Database.php');
require_once(__DIR__ . '/../store/Store.php');
require_once(__DIR__ . '/../Evaluate.php');
15
require_once(__DIR__ . '/../report/Sendmail.php');
16
17
18
19
20

/**
 * Class formAction
 * @package qfq
 */
21
class FormAction {
22
23

//    private $feSpecNative = array(); // copy of all formElement.class='native' of the loaded form
24
25
26
27
28
    /**
     * @var Evaluate instantiated class
     */
    protected $evaluate = null;  // copy of the loaded form
    private $formSpec = array();
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    private $primaryTableName = '';
    /**
     * @var Database
     */
    private $db = null;
    /**
     * @var Store
     */
    private $store = null;

    /**
     * @param array $formSpec
     * @param Database $db
     * @param bool|false $phpUnit
     */
    public function __construct(array $formSpec, Database $db, $phpUnit = false) {
        $this->formSpec = $formSpec;
46
        $this->primaryTableName = Support::setIfNotSet($formSpec, F_TABLE_NAME);
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

        $this->db = $db;

        $this->store = Store::getInstance('', $phpUnit);

        $this->evaluate = new Evaluate($this->store, $this->db);

    }

    /**
     * @param integer $recordId
     * @param array $feSpecAction
     * @param string $feTypeList
     *         On FormLoad: FE_TYPE_BEFORE_LOAD, FE_TYPE_AFTER_LOAD
     *         Before Save: FE_TYPE_BEFORE_SAVE, FE_TYPE_BEFORE_INSERT, FE_TYPE_BEFORE_UPDATE, FE_TYPE_BEFORE_DELETE
     *         After Save: FE_TYPE_AFTER_SAVE, FE_TYPE_AFTER_INSERT, FE_TYPE_AFTER_UPDATE, FE_TYPE_AFTER_DELETE
63
     * @return bool: true if there are potential changes on the DB like fired SQL statements, else false.
64
65
66
67
68
69
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     */
    public function elements($recordId, array $feSpecAction, $feTypeList) {

70
71
        $flagModified = false;

72
73
74
        // Iterate over all Action FormElements
        foreach ($feSpecAction as $fe) {

75
            $fe = HelperFormElement::initActionFormElement($fe);
76

77
            // Only process FE elements of types listed in $feTypeList. Skip all other
78
79
80
81
            if (false === Support::findInSet($fe[FE_TYPE], $feTypeList)) {
                continue;
            }

82
83
84
            // Preparation for Log, Debug
            $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($fe), STORE_SYSTEM);

85
86
87
88
89
90
91
92
            switch ($fe[FE_TYPE]) {
                case FE_TYPE_BEFORE_LOAD:
                case FE_TYPE_AFTER_LOAD:
                case FE_TYPE_AFTER_DELETE:  # Main record is already deleted. Do not try to load it again.
                    break;
                default:
                    // Always work on recent data: previous actions might have modified the data.
                    $this->fillStoreRecord($this->primaryTableName, $recordId);
93
94
            }

95
96
97
98
            if (!$this->checkRequiredList($fe)) {
                continue;
            }

99
100
101
102
103
            if ($fe[FE_TYPE] === FE_TYPE_SENDMAIL) {
                $this->sendMail($fe);
                //no further processing of current element necessary.
                continue;
            }
104
105
106
107

            $this->validate($fe);

            $this->doSlave($fe, $recordId);
108
109

            $flagModified = true;
110
        }
111
112

        return $flagModified;
113
114
115
    }

    /**
116
117
     * Copy the current primary record to STORE_RECORD
     *
118
119
     * @param $table
     * @param $recordId
120
     * @throws CodeException
121
     * @throws DbException
122
123
     * @throws UserFormException
     */
124
    private function fillStoreRecord($table, $recordId) {
125

126
127
        if (!is_string($table) || $table === '') {
            throw new UserFormException("");
128
        }
129

130
131
        if ($recordId !== false && $recordId > 0) {
            $record = $this->db->sql("SELECT * FROM $table WHERE id = ?", ROW_EXPECT_1, [$recordId]);
132
            $this->store->setStore($record, STORE_RECORD, true);
133
        }
134
    }
135

136
137
138
139
140
141
142
143
144
    /**
     * Process all FormElements given in the `requiredList` identified be their name.
     * If none is empty in STORE_FORM return true, else false.
     * If none FormElement is specified, return true.
     *
     * @param array $fe
     * @return bool  true if none FE is specified or all specified are non empty.
     */
    private function checkRequiredList(array $fe) {
145

146
147
        if (!isset($fe[FE_REQUIRED_LIST]) || $fe[FE_REQUIRED_LIST] === '') {
            return true;
148
149
        }

150
151
152
153
154
155
156
157
158
        $arr = explode(',', $fe[FE_REQUIRED_LIST]);
        foreach ($arr as $key) {

            $key = trim($key);
            $val = $this->store->getVar($key, STORE_FORM);

            if ($val === false || $val === '' || $val === '0') {
                return false;
            }
159
160
        }

161
        return true;
162
163
    }

164
165
166
167
168
169
170
171
172
173
    /**
     * @param array $feSpecAction
     */
    private function sendMail(array $feSpecAction) {

        $mail[SENDMAIL_IDX_RECEIVER] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_TO]);
        $mail[SENDMAIL_IDX_SENDER] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_FROM]);
        $mail[SENDMAIL_IDX_SUBJECT] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_SUBJECT]);
        $mail[SENDMAIL_IDX_BODY] = $this->evaluate->parse($feSpecAction[FE_VALUE]);
        $mail[SENDMAIL_IDX_REPLY_TO] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_REPLY_TO]);
174
        $mail[SENDMAIL_IDX_FLAG_AUTO_SUBMIT] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_FLAG_AUTO_SUBMIT]) === 'off' ? 'off' : 'on';
175
176
177
178
179
180
181
182
183
184
        $mail[SENDMAIL_IDX_GR_ID] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_GR_ID]);
        $mail[SENDMAIL_IDX_X_ID] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_X_ID]);
        $mail[SENDMAIL_IDX_RECEIVER_CC] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_CC]);
        $mail[SENDMAIL_IDX_RECEIVER_BCC] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_BCC]);
        $mail[SENDMAIL_IDX_SRC] = "FormId: " . $feSpecAction['formId'] . ", FormElementId: " . $feSpecAction['id'];

        // Mail: send
        new Sendmail($mail);
    }

185
186
187
188
    /**
     * If there is a query defined in fe.parameter.FE_SQL_VALIDATE: fire them.
     * Count the selected records and compare them with fe.parameter.FE_EXPECT_RECORDS.
     * If match: everything is fine, do nothing.
189
     * Else throw UserFormException with error message of fe.parameter.FE_MESSAGE_FAIL
190
191
192
193
194
195
196
     *
     * @param array $fe
     * @throws UserFormException
     */
    private function validate(array $fe) {

        // Is there something to check?
197
        if ($fe[FE_SQL_VALIDATE] === '') {
198
199
200
            return;
        }

201
        $expect = $this->evaluate->parse($fe[FE_EXPECT_RECORDS]);
202

203
        if ($fe[FE_MESSAGE_FAIL] === '') {
204
205
206
207
208
209
210
211
212
            throw new UserFormException("Missing error message. Column: " . FE_MESSAGE_FAIL, ERROR_MISSING_MESSAGE_FAIL);
        }

        // Do the check
        $result = $this->evaluate->parse($fe[FE_SQL_VALIDATE]);
        if (!is_array($result)) {
            throw new UserFormException("Expected an array for '" . FE_SQL_VALIDATE . "', got a scalar. Please check for {{!...", ERROR_EXPECTED_ARRAY);
        }

213
214
215
216
217
218
        // If there is at least one record count given, who matches: return 'check succeeded'
        $countRecordsArr = explode(',', $expect);
        foreach ($countRecordsArr AS $count) {
            if (count($result) == $count) {
                return; // check succesfully passed
            }
219
220
        }

221
        $msg = $this->evaluate->parse($fe[FE_MESSAGE_FAIL]); // Replace possible dynamic parts
222
223
224
225
226

        // Throw user defineable error message
        throw new UserFormException($msg, ERROR_REPORT_FAILED_ACTION);
    }

227
228
229
230
231
232
233
234
235
236
237
    /**
     * Create the slave record. First try to evaluate a slaveId. Depending if the slaveId > 0 choose `sqlUpdate` or `sqlInsert`
     *
     * @param array $fe
     * @return int
     * @throws CodeException
     * @throws UserFormException
     */
    private function doSlave(array $fe, $recordId) {

        // Get the slaveId
238
        $slaveId = $this->evaluate->parse($fe[FE_SLAVE_ID]);
239
240
241
242
243
244
245
246
247
248
249

        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
            $slaveId = $this->store->getVar($fe[FE_NAME], STORE_RECORD);
        }

        if ($slaveId === '' || $slaveId === false) {
            $slaveId = 0;
        }

        // Store the slaveId: it's used and replaced in the update statement.
250
251
252
253
        $this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);

        // If given: fire a sqlBefore query
        $this->evaluate->parse($fe[FE_SQL_BEFORE]);
254
255
256
257

        // Fire slave query
        if ($slaveId == 0) {
            $slaveId = $this->evaluate->parse($fe[FE_SQL_INSERT]);
258
259
            // Store the slaveId: might be used later
            $this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);
260
261
262
263
264
265
        } else {
            $this->evaluate->parse($fe[FE_SQL_UPDATE]);
        }

        // Check if there is a column with the same name as the 'action'-FormElement.
        if (false !== $this->store->getVar($fe[FE_NAME], STORE_RECORD)) {
266
            // After an insert or update, propagate the (new) slave id to the master record.
267
268
269
            $this->db->sql("UPDATE " . $this->primaryTableName . " SET " . $fe[FE_NAME] . " = $slaveId WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]);
        }

270
        // If given: fire a delete query
271
272
        $this->evaluate->parse($fe[FE_SQL_DELETE]);

273
274
275
276
        // If given: fire a sqlAfter query
        $this->evaluate->parse($fe[FE_SQL_AFTER]);


277
278
        return $slaveId;
    }
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298

    /**
     * Set all necessary keys - subsequent 'isset()' are not necessary anymore.
     *
     * @param array $fe
     * @return array
     */
    private function initActionFormElement(array $fe) {

        $list = [FE_TYPE, FE_SLAVE_ID, FE_SQL_VALIDATE, FE_SQL_BEFORE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE,
            FE_SQL_AFTER, FE_EXPECT_RECORDS, FE_REQUIRED_LIST, FE_MESSAGE_FAIL, FE_SENDMAIL_TO, FE_SENDMAIL_CC,
            FE_SENDMAIL_BCC, FE_SENDMAIL_FROM, FE_SENDMAIL_SUBJECT, FE_SENDMAIL_REPLY_TO, FE_SENDMAIL_FLAG_AUTO_SUBMIT,
            FE_SENDMAIL_GR_ID, FE_SENDMAIL_X_ID];

        foreach ($list as $key) {
            Support::setIfNotSet($fe, $key);
        }

        return $fe;
    }
299
}