PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | admin/helpers/src/mvc/model.php +506 -0 1.6.2 → 1.8.16 View file →
@@ -1,0 +1,506 @@
1 +<?php
2 +/**
3 + * @package VikBooking
4 + * @subpackage core
5 + * @author E4J s.r.l.
6 + * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 + * @link https://vikwp.com
9 + */
10 +
11 +// No direct access
12 +defined('ABSPATH') or die('No script kiddies please!');
13 +
14 +/**
15 + * VikBooking MVC base model declaration.
16 + *
17 + * @since 1.15.10 (J) - 1.5.10 (WP)
18 + */
19 +abstract class VBOMvcModel extends JObject
20 +{
21 + /**
22 + * Cached used to store the supported columns of the requested database tables.
23 + *
24 + * @var string
25 + */
26 + protected static $fields = [];
27 +
28 + /**
29 + * The database table name.
30 + *
31 + * @var string
32 + */
33 + protected $tableName = null;
34 +
35 + /**
36 + * The database table primary key name.
37 + *
38 + * @var string
39 + */
40 + protected $tableKeyName = 'id';
41 +
42 + /**
43 + * Returns a new instance of the requested model.
44 + *
45 + * @param string $name The model name.
46 + * @param array $options The model configuration.
47 + *
48 + * @return VBOMvcModel
49 + */
50 + public static function getInstance($name, array $options = [])
51 + {
52 + /**
53 + * Trigger hook to allow third party plugins to extend the default models in VikBooking without
54 + * altering a single line of code from the core of the plugin.
55 + *
56 + * It is either possible to swap the name or to return a new instance of VBOMvcModel.
57 + *
58 + * @param string &$name A reference to the requested model.
59 + * @param array &$options A reference to the model configuration.
60 + *
61 + * @return mixed
62 + */
63 + $results = VBOFactory::getPlatform()->getDispatcher()->filter('onBeforeCreateModelVikBooking', [&$name, &$options]);
64 +
65 + // filter the returned values to take only the first valid instance
66 + $results = array_filter($results, function($data)
67 + {
68 + return $data instanceof VBOMvcModel;
69 + });
70 +
71 + if ($results)
72 + {
73 + // class returned by a plugin, directly use it
74 + return reset($results);
75 + }
76 +
77 + // fallback to the default classes in VikBooking
78 + $suffix = str_replace('_', ' ', $name);
79 + $suffix = str_replace(' ', '', ucwords($suffix));
80 +
81 + $modelClass = 'VBOModel' . $suffix;
82 +
83 + if (!class_exists($modelClass))
84 + {
85 + throw new Exception(sprintf('Model [%s] not found', $name), 404);
86 + }
87 +
88 + return new $modelClass($options);
89 + }
90 +
91 + /**
92 + * Returns the model name.
93 + *
94 + * @return string
95 + */
96 + public function getName()
97 + {
98 + if (preg_match("/^VBOModel([a-z0-9_]+)$/i", get_class($this), $match))
99 + {
100 + return strtolower($match[1]);
101 + }
102 +
103 + // unable to extract model name from class, use the file name
104 + return basename(__FILE__, '.php');
105 + }
106 +
107 + /**
108 + * Basic item loading implementation.
109 + *
110 + * @param mixed $pk An optional primary key value to load the row by, or an array of fields to match.
111 + * If not set the instance property value is used.
112 + *
113 + * @return ?object The record object on success, null otherwise.
114 + */
115 + public function getItem($pk)
116 + {
117 + $db = JFactory::getDbo();
118 +
119 + $query = $db->getQuery(true)
120 + ->select('*')
121 + ->from($db->qn($this->tableName));
122 +
123 + if (is_array($pk))
124 + {
125 + foreach ($pk as $column => $value)
126 + {
127 + $query->where($db->qn($column) . ' = ' . $db->q($value));
128 + }
129 + }
130 + else
131 + {
132 + $query->where($db->qn($this->tableKeyName) . ' = ' . (int) $pk);
133 + }
134 +
135 + $db->setQuery($query, 0, 1);
136 +
137 + return $db->loadObject();
138 + }
139 +
140 + /**
141 + * Items loading implementation.
142 + *
143 + * @param array $clauses List of associative columns to fetch
144 + * (column => [operator, value] or column => value)
145 + * @param int $start Query limit start.
146 + * @param int $lim Query limit value.
147 + * @param array $cols Optional list of columns to fetch.
148 + * @param array $ordering Optional associative list of ordering columns.
149 + *
150 + * @return array List of record objects.
151 + *
152 + * @since 1.18.8 (J) - 1.8.8 (WP)
153 + */
154 + public function getItems(array $clauses = [], int $start = 0, int $lim = 0, array $cols = [], array $ordering = [])
155 + {
156 + $db = JFactory::getDbo();
157 +
158 + $q = $db->getQuery(true);
159 +
160 + if (!$cols)
161 + {
162 + $q->select('*');
163 + }
164 + else
165 + {
166 + $q->select(array_map([$db, 'qn'], $cols));
167 + }
168 +
169 + $q->from($db->qn($this->tableName));
170 +
171 + foreach ($clauses as $column => $data)
172 + {
173 + if (!is_array($data))
174 + {
175 + // convert structure
176 + $data = ['value' => $data];
177 + }
178 +
179 + if (is_null($data['value']))
180 + {
181 + $q->where($db->qn($column) . ' IS NULL');
182 + }
183 + else
184 + {
185 + $q->where($db->qn($column) . ' ' . ($data['operator'] ?? '=') . ' ' . $db->q($data['value']));
186 + }
187 + }
188 +
189 + if ($ordering)
190 + {
191 + // sort by given columns
192 + foreach ($ordering as $orderColumn => $orderSort)
193 + {
194 + if (!is_string($orderSort))
195 + {
196 + continue;
197 + }
198 +
199 + // set ordering column
200 + $q->order($db->qn($orderColumn) . ' ' . (!strcasecmp($orderSort, 'ASC') ? 'ASC' : 'DESC'));
201 + }
202 + }
203 + else
204 + {
205 + // default ordering column
206 + $q->order($db->qn($this->tableKeyName) . ' ASC');
207 + }
208 +
209 + $db->setQuery($q, $start, $lim);
210 +
211 + return $db->loadObjectList();
212 + }
213 +
214 + /**
215 + * Method to save the form data.
216 + *
217 + * @param mixed $data The form data.
218 + *
219 + * @return mixed The primary key of the saved item, false otherwise.
220 + */
221 + public function save($data)
222 + {
223 + $data = (array) $data;
224 +
225 + // let the children bind the data array before save
226 + if (!$this->preflight($data))
227 + {
228 + return false;
229 + }
230 +
231 + // check if we have an empty record
232 + $isNew = empty($data[$this->tableKeyName]);
233 +
234 + // store the object within the database
235 + $pk = $this->store((array) $data);
236 +
237 + if (!$pk)
238 + {
239 + return false;
240 + }
241 +
242 + // let the children perform extra actions after saving
243 + $this->postflight((array) $data, $isNew);
244 +
245 + return $pk;
246 + }
247 +
248 + /**
249 + * Method to delete one or more records.
250 + *
251 + * @param mixed $pks An array of record primary keys.
252 + *
253 + * @return bool True if successful, false if an error occurs.
254 + */
255 + public function delete($pks)
256 + {
257 + if (!$pks)
258 + {
259 + // nothing to delete
260 + return false;
261 + }
262 +
263 + if (!is_array($pks))
264 + {
265 + // wrap in an array
266 + $pks = [$pks];
267 + }
268 +
269 + $app = JFactory::getApplication();
270 +
271 + $affected = true;
272 +
273 + foreach ($pks as $pk)
274 + {
275 + // fetch item details
276 + $item = $this->getItem($pk);
277 +
278 + if (!$item)
279 + {
280 + continue;
281 + }
282 +
283 + /**
284 + * Runs before deleting the given data.
285 + * It is possible to validate here whether the item should be deleted or not.
286 + *
287 + * @param object $data The record to delete.
288 + * @param self $model The current model.
289 + *
290 + * @return bool False to abort the deleting process.
291 + *
292 + * @since 1.5.10
293 + */
294 + $results = $app->triggerEvent('onBeforeDeleteVikBooking' . ucfirst($this->getName()), [$item, $this]);
295 +
296 + // check whether a plugin aborted the deleting process
297 + if (in_array(false, $results, true))
298 + {
299 + continue;
300 + }
301 +
302 + // remove item
303 + $result = $this->remove($item);
304 +
305 + /**
306 + * Runs after deleting the given data.
307 + * It is possible to perform here some extra actions.
308 + *
309 + * @param object $data The deleted item.
310 + * @param bool $success True in case of success, false otherwise.
311 + * @param self $model The current model.
312 + *
313 + * @return void
314 + *
315 + * @since 1.5.10
316 + */
317 + $app->triggerEvent('onAfterDeleteVikBooking' . ucfirst($this->getName()), [$item, $result, $this]);
318 +
319 + $affected = $affected || $result;
320 + }
321 +
322 + return $affected;
323 + }
324 +
325 + /**
326 + * Runs before saving the given data.
327 + * It is possible to inject here additional properties to save.
328 + *
329 + * @param array &$data A reference to the data to save.
330 + *
331 + * @return bool True on success, false otherwise.
332 + */
333 + protected function preflight(array &$data)
334 + {
335 + $app = JFactory::getApplication();
336 +
337 + /**
338 + * Runs before saving the given data.
339 + * It is possible to inject here additional properties to save.
340 + *
341 + * @param array &$data A reference to the data to save.
342 + * @param self $model The current model.
343 + *
344 + * @return bool False to abort the saving process.
345 + *
346 + * @since 1.5.10
347 + */
348 + $results = $app->triggerEvent('onBeforeSaveVikBooking' . ucfirst($this->getName()), [&$data, $this]);
349 +
350 + // check whether a plugin aborted the saving process
351 + if (in_array(false, $results, true))
352 + {
353 + return false;
354 + }
355 +
356 + return true;
357 + }
358 +
359 + /**
360 + * Converts the array data into an object compatible with the database
361 + * structure of the specified table.
362 + *
363 + * @param array $data The requested data.
364 + *
365 + * @return object The object ready to be saved.
366 + */
367 + protected function prepareSaveData(array $data)
368 + {
369 + $table = new stdClass;
370 +
371 + // bind only the columns supported by the database table
372 + foreach ($this->getTableColumns() as $field => $type)
373 + {
374 + if (isset($data[$field]))
375 + {
376 + // always encode in JSON format non-scalar values
377 + if (!is_scalar($data[$field]))
378 + {
379 + $data[$field] = json_encode($data[$field]);
380 + }
381 +
382 + $table->{$field} = $data[$field];
383 + }
384 + }
385 +
386 + return $table;
387 + }
388 +
389 + /**
390 + * Saves the specified data into the database.
391 + *
392 + * @param array $data The requested data.
393 + *
394 + * @return mixed The primary key value of the saved record (true if missing).
395 + * Returns false in case of failure.
396 + */
397 + final protected function store(array $data)
398 + {
399 + // construct the object that will be saved
400 + $table = $this->prepareSaveData($data);
401 +
402 + if (!$table)
403 + {
404 + return false;
405 + }
406 +
407 + $db = JFactory::getDbo();
408 +
409 + if (!empty($table->{$this->tableKeyName}))
410 + {
411 + // update needed
412 + $res = $db->updateObject($this->tableName, $table, $this->tableKeyName);
413 + }
414 + else
415 + {
416 + // insert needed
417 + $res = $db->insertObject($this->tableName, $table, $this->tableKeyName);
418 + }
419 +
420 + if (!$res)
421 + {
422 + // something went wrong while saving the record
423 + return false;
424 + }
425 +
426 + // check again whether the PK has been filled
427 + if (empty($table->{$this->tableKeyName}))
428 + {
429 + // no PK, just return true
430 + return true;
431 + }
432 +
433 + return $table->{$this->tableKeyName};
434 + }
435 +
436 + /**
437 + * Runs after saving the given data.
438 + * It is possible to perform here some extra actions.
439 + *
440 + * @param array $data The saved data.
441 + * @param bool True in case of insert, false in case of update.
442 + *
443 + * @return void
444 + */
445 + protected function postflight(array $data, $isNew)
446 + {
447 + $app = JFactory::getApplication();
448 +
449 + /**
450 + * Runs after saving the given data.
451 + * It is possible to perform here some extra actions.
452 + *
453 + * @param array $data The saved data.
454 + * @param bool $isNew True in case of insert, false in case of update.
455 + * @param self $model The current model.
456 + *
457 + * @return void
458 + *
459 + * @since 1.5.10
460 + */
461 + $app->triggerEvent('onAfterSaveVikBooking' . ucfirst($this->getName()), [$data, $isNew, $this]);
462 + }
463 +
464 + /**
465 + * Removes the specified item from the database.
466 + *
467 + * @param object $item The record to delete.
468 + *
469 + * @return bool True if deleted, false otherwise.
470 + */
471 + protected function remove($item)
472 + {
473 + $db = JFactory::getDbo();
474 +
475 + $query = $db->getQuery(true)
476 + ->delete($db->qn($this->tableName))
477 + ->where($db->qn($this->tableKeyName) . ' = ' . $db->q($item->{$this->tableKeyName}));
478 +
479 + $db->setQuery($query);
480 + $db->execute();
481 +
482 + return (bool) $db->getAffectedRows();
483 + }
484 +
485 + /**
486 + * Returns all the columns supported by the database table of this model.
487 + *
488 + * @return array
489 + */
490 + public function getTableColumns()
491 + {
492 + if (!isset(static::$fields[$this->tableName]))
493 + {
494 + try
495 + {
496 + static::$fields[$this->tableName] = JFactory::getDbo()->getTableColumns($this->tableName);
497 + }
498 + catch (Exception $e)
499 + {
500 + static::$fields[$this->tableName] = [];
501 + }
502 + }
503 +
504 + return static::$fields[$this->tableName];
505 + }
506 +}