DragAndDrop.php 7.24 KB
Newer Older
1
2
3
4
5
6
7
8
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 3/13/17
 * Time: 9:29 PM
 */

Marc Egger's avatar
Marc Egger committed
9
namespace IMATHUZH\Qfq\Core\Form;
10

Marc Egger's avatar
Marc Egger committed
11
12
13
14
use IMATHUZH\Qfq\Core\Store\Store;
use IMATHUZH\Qfq\Core\Database\Database;
use IMATHUZH\Qfq\Core\Evaluate;
 
15
16
17
18
19
20
21
22
23


/**
 * Class DragAndDrop
 * @package qfq
 */
class DragAndDrop {

    /**
Carsten  Rose's avatar
Carsten Rose committed
24
     * @var Database
25
26
27
28
     */
    private $db = null;

    /**
29
     * @var Store
30
     */
31
    private $store = null;
32

33
34
35
36
    /**
     * @var Evaluate instantiated class
     */
    protected $evaluate = null;  // copy of the loaded form
37

38
39
40
41
42
    /**
     * @var array
     */
    private $formSpec = null;

43
    /**
44
45
46
     * @param array $formSpec F_TABLE_NAME, F_DRAG_AND_DROP_ORDER_SQL, F_DRAG_AND_DROP_INTERVAL
     * @param bool|false $phpUnit
     *
Marc Egger's avatar
Marc Egger committed
47
48
49
50
     * @throws \CodeException
     * @throws \DbException
     * @throws \UserFormException
     * @throws \UserReportException
51
     */
52
    public function __construct(array $formSpec = array(), $phpUnit = false) {
53

54
        #TODO: rewrite $phpUnit to: "if (!defined('PHPUNIT_QFQ')) {...}"
55
56
        $this->formSpec = $formSpec;

Carsten  Rose's avatar
Carsten Rose committed
57
        $dbIndex = DB_INDEX_DEFAULT;  //Hier muss noch die aktuelle DB ermittelt werden (kann im Form angegeben sein) - Gerade im Formular FORM Editor genau testen!
58
59
        $this->db = new Database($dbIndex);

60
        $this->store = Store::getInstance('', $phpUnit);
61
//        $this->evaluate = new Evaluate($this->store, $this->db);
62
63
64
    }

    /**
65
     * Reorder the elements according formSpec[F_DRAG_AND_DROP_ORDER_SQL]
66
     *
67
     * @return array|int
Marc Egger's avatar
Marc Egger committed
68
69
70
     * @throws \CodeException
     * @throws \DbException
     * @throws \UserFormException
71
     */
72
    public function process() {
73

74
        if (!is_array($this->formSpec[F_DRAG_AND_DROP_ORDER_SQL]) || count($this->formSpec[F_DRAG_AND_DROP_ORDER_SQL]) == 0) {
Marc Egger's avatar
Marc Egger committed
75
            throw new \UserFormException('Reorder SQL failed - expect at least one record, but got nothing. Check: Form.parameter.' . F_DRAG_AND_DROP_ORDER_SQL, ERROR_DND_EMPTY_REORDER_SQL);
76
77
        }

78
79
80
        $dragId = $this->store->getVar(DND_DRAG_ID, STORE_CLIENT, SANITIZE_ALLOW_ALNUMX);
        $setTo = $this->store->getVar(DND_SET_TO, STORE_CLIENT, SANITIZE_ALLOW_ALNUMX);
        $hoverId = $this->store->getVar(DND_HOVER_ID, STORE_CLIENT, SANITIZE_ALLOW_ALNUMX);
81

82
83
        $orderInterval = empty($this->formSpec[F_ORDER_INTERVAL]) ? 1 : $this->formSpec[F_ORDER_INTERVAL];
        $orderColumn = empty($this->formSpec[F_ORDER_COLUMN]) ? F_ORDER_COLUMN_NAME : $this->formSpec[F_ORDER_COLUMN];
84

85
86
87
//        if (!is_array($this->formSpec[F_DRAG_AND_DROP_ORDER_SQL])) {
//            return [];
//        }
88

89
90
        $data = $this->reorder($this->formSpec[F_DRAG_AND_DROP_ORDER_SQL], $dragId, $setTo, $hoverId, $orderColumn,
            $orderInterval, $this->formSpec[F_TABLE_NAME]);
91

92
        return $data;
93
94
95
    }

    /**
96
97
98
99
100
101
102
103
104
105
106
107
     * Calculate new ord values. The array rows$ contains the old order with
     * [
     *   [
     *     [DND_COLUMN_ID] => 1,
     *     [DND_COLUMN_ORD] => 10
     *   ], [
     *     [DND_COLUMN_ID] => 2,
     *     [DND_COLUMN_ORD] => 20
     *   ],
     *   ...
     * ]
     *
108
109
110
111
112
113
114
     * @param array $rows Array with id/ord in the old order.
     * @param int $dragId Id of the element which has been drag'ed
     * @param string $setTo DND_SET_TO_BEFORE|DND_SET_TO_AFTER   Indicates if the drop zone is before or after the $hoverId
     * @param int $hoverId Id of element where the drag'ed element has been dropped on.
     * @param string $orderColumn Table column where to save the new calculated order.
     * @param int $orderInterval Order increment.
     * @param string $tableName Table name where to update the order records.
115
116
     * @return array          Array with html-id references to update order values in the browser. Check PROTOCOL.md for 'element-update'.
     *
Marc Egger's avatar
Marc Egger committed
117
118
119
     * @throws \CodeException
     * @throws \DbException
     * @throws \UserFormException
120
     */
121
122
123
    private function reorder(array $rows, $dragId, $setTo, $hoverId, $orderColumn, $orderInterval, $tableName) {
        $ord = $orderInterval;
        $ordDragOld = -1;
124
        $data = array();
125
126
        $nameId = false;
        $nameOrd = false;
127

128
        // Reorder. Get index for 'drag' and 'hover'
129
        foreach ($rows as $key => $row) {
130

131
132
133
134
135
136
137
138
            if ($nameId === false) {
                $nameId = $this->getFinalColumnName(DND_COLUMN_ID, $row);
                $nameOrd = $this->getFinalColumnName(DND_COLUMN_ORD, $row);
            }

            // The dragged element: skip old position.
            if ($row[$nameId] == $dragId) {
                $ordDragOld = $row[$nameOrd];
139
140
                continue;
            }
141

142
143
            // The dragged element: new position.
            if ($row[$nameId] == $hoverId) {
144
145
146

                switch ($setTo) {
                    case DND_SET_TO_BEFORE:
147
                        $data = $this->setNewOrder($tableName, $orderColumn, $dragId, $ordDragOld, $ord, $data);
148
                        $ord += $orderInterval;
149
                        $data = $this->setNewOrder($tableName, $orderColumn, $row[$nameId], $row[$nameOrd], $ord, $data);
150
151
152
                        break;

                    case DND_SET_TO_AFTER:
153
                        $data = $this->setNewOrder($tableName, $orderColumn, $row[$nameId], $row[$nameOrd], $ord, $data);
154
                        $ord += $orderInterval;
155
                        $data = $this->setNewOrder($tableName, $orderColumn, $dragId, $ordDragOld, $ord, $data);
156
157
158
                        break;

                    default:
Marc Egger's avatar
Marc Egger committed
159
                        throw new \CodeException(json_encode([ERROR_MESSAGE_TO_USER => 'Unknown "setTo" string', ERROR_MESSAGE_TO_DEVELOPER => "Token found: " . $setTo]), ERROR_UNKNOWN_TOKEN);
160
161
                }
            } else {
162
                $data = $this->setNewOrder($tableName, $orderColumn, $row[$nameId], $row[$nameOrd], $ord, $data);
163
164
            }
            $ord += $orderInterval;
165
        }
166
167

        return $data;
168
    }
169

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
    /**
     * Check if there is a column called '_' . $name. If yes, return that name, else return $name.
     * If none is found, throw an exception.
     *
     * @param $name
     * @param $row
     * @return string
     * @throws \UserFormException
     */
    private function getFinalColumnName($name, $row) {

        if (!isset($row[$name]) && !isset($row['_' . $name])) {
            throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => "Missing column '$name' or '_$name'", ERROR_MESSAGE_TO_DEVELOPER => "Check your DND SQL statement"]),
                ERROR_MISSING_REQUIRED_PARAMETER);
        }

        return isset($row['_' . $name]) ? '_' . $name : $name;
    }

189
    /**
190
191
192
193
194
195
196
     * @param string $tableName
     * @param string $orderColumn
     * @param int $id
     * @param int $ordOld
     * @param int $ordNew
     * @param array $data
     * @return array
Marc Egger's avatar
Marc Egger committed
197
198
199
     * @throws \CodeException
     * @throws \DbException
     * @throws \UserFormException
200
     */
201
    private function setNewOrder($tableName, $orderColumn, $id, $ordOld, $ordNew, array $data) {
202

203
        if ($ordNew == $ordOld) {
204
            return $data;
205
206
        }

207
        $this->db->sql("UPDATE $tableName SET $orderColumn=? WHERE id=?", ROW_REGULAR, [$ordNew, $id]);
208
209
210
211
212

        // Converting to string is necessary: JSON detects int else.
        $data[API_ELEMENT_UPDATE][DND_ORD_HTML_ID_PREFIX . $id][API_ELEMENT_CONTENT] = (string)$ordNew;

        return $data;
213
214
    }
}