FormAction.php 9.99 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
76
            $fe = $this->initActionFormElement($fe);

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
     * Set all necessary keys - subsequent 'isset()' are not necessary anymore.
117
118
119
120
121
     *
     * @param array $fe
     * @return array
     */
    private function initActionFormElement(array $fe) {
122
123
124
125
126

        $list = [FE_TYPE, FE_SLAVE_ID, FE_SQL_VALIDATE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE, 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];

127
128
129
        foreach ($list as $key) {
            Support::setIfNotSet($fe, $key);
        }
130

131
132
133
        return $fe;
    }

134
    /**
135
136
     * Copy the current primary record to STORE_RECORD
     *
137
138
     * @param $table
     * @param $recordId
139
     * @throws CodeException
140
     * @throws DbException
141
142
     * @throws UserFormException
     */
143
    private function fillStoreRecord($table, $recordId) {
144

145
146
        if (!is_string($table) || $table === '') {
            throw new UserFormException("");
147
        }
148

149
150
151
        if ($recordId !== false && $recordId > 0) {
            $record = $this->db->sql("SELECT * FROM $table WHERE id = ?", ROW_EXPECT_1, [$recordId]);
            $this->store->setVarArray($record, STORE_RECORD, true);
152
        }
153
    }
154

155
156
157
158
159
160
161
162
163
    /**
     * 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) {
164

165
166
        if (!isset($fe[FE_REQUIRED_LIST]) || $fe[FE_REQUIRED_LIST] === '') {
            return true;
167
168
        }

169
170
171
172
173
174
175
176
177
        $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;
            }
178
179
        }

180
        return true;
181
182
    }

183
184
185
186
187
188
189
190
191
192
    /**
     * @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]);
193
        $mail[SENDMAIL_IDX_FLAG_AUTO_SUBMIT] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_FLAG_AUTO_SUBMIT]) === 'off' ? 'off' : 'on';
194
195
196
197
198
199
200
201
202
203
        $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);
    }

204
205
206
207
    /**
     * 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.
208
     * Else throw UserFormException with error message of fe.parameter.FE_MESSAGE_FAIL
209
210
211
212
213
214
215
     *
     * @param array $fe
     * @throws UserFormException
     */
    private function validate(array $fe) {

        // Is there something to check?
216
        if ($fe[FE_SQL_VALIDATE] === '') {
217
218
219
            return;
        }

220
        $expect = $this->evaluate->parse($fe[FE_EXPECT_RECORDS]);
221

222
        if ($fe[FE_MESSAGE_FAIL] === '') {
223
224
225
226
227
228
229
230
231
            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);
        }

232
233
234
235
236
237
        // 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
            }
238
239
        }

240
        $msg = $this->evaluate->parse($fe[FE_MESSAGE_FAIL]); // Replace possible dynamic parts
241
242
243
244
245

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

246
247
248
249
250
251
252
253
254
255
256
    /**
     * 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
257
        $slaveId = $this->evaluate->parse($fe[FE_SLAVE_ID]);
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

        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.
        $this->store->setVar(ACTION_KEYWORD_SLAVE_ID, $slaveId, STORE_VAR, true);

        // Fire slave query
        if ($slaveId == 0) {
            $slaveId = $this->evaluate->parse($fe[FE_SQL_INSERT]);
274
275
            // Store the slaveId: it's used and replaced in the update statement.
            $this->store->setVar(ACTION_KEYWORD_SLAVE_ID, $slaveId, STORE_VAR, true);
276
277
278
279
280
281
        } 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)) {
282
            // After an insert or update, propagate the (new) slave id to the master record.
283
284
285
            $this->db->sql("UPDATE " . $this->primaryTableName . " SET " . $fe[FE_NAME] . " = $slaveId WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]);
        }

286
        // If given: fire a delete query
287
288
        $this->evaluate->parse($fe[FE_SQL_DELETE]);

289
290
        return $slaveId;
    }
291
}