| @@ -1,0 +1,336 @@ | ||
| 1 | +<?php | |
| 2 | +/** | |
| 3 | + * @package VikBooking | |
| 4 | + * @subpackage core | |
| 5 | + * @author Alessio Gaggii - E4J s.r.l. | |
| 6 | + * @copyright Copyright (C) 2022 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 | + * Helper class to support custom pax fields data collection types. | |
| 16 | + * | |
| 17 | + * @since 1.15.0 (J) - 1.5.0 (WP) | |
| 18 | + */ | |
| 19 | +final class VBOCheckinPax | |
| 20 | +{ | |
| 21 | + /** | |
| 22 | + * Container of all pax fields collection instances. | |
| 23 | + * | |
| 24 | + * @var array | |
| 25 | + */ | |
| 26 | + private static $instances = array(); | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * Data container of all pax fields collection instances. | |
| 30 | + * | |
| 31 | + * @var array | |
| 32 | + */ | |
| 33 | + private static $instances_data = array(); | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * Retrieves the list of back-end pax fields for an extended collection type. | |
| 37 | + * | |
| 38 | + * @param string $type the pax data type representing the class. | |
| 39 | + * | |
| 40 | + * @return array the list of pax fields to collect in the back-end. | |
| 41 | + */ | |
| 42 | + public static function getFields($type = '') | |
| 43 | + { | |
| 44 | + // default empty containers | |
| 45 | + $labels = []; | |
| 46 | + $attributes = []; | |
| 47 | + | |
| 48 | + if (empty($type)) { | |
| 49 | + return [$labels, $attributes]; | |
| 50 | + } | |
| 51 | + | |
| 52 | + // invoke custom pax fields object | |
| 53 | + $paxf_obj = self::getInstance($type); | |
| 54 | + if (!$paxf_obj) { | |
| 55 | + return [$labels, $attributes]; | |
| 56 | + } | |
| 57 | + | |
| 58 | + return $paxf_obj->listFields(); | |
| 59 | + } | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * Retrieves the list of pre-checkin pax fields for an extended collection type. | |
| 63 | + * | |
| 64 | + * @param string $type the pax data type representing the class. | |
| 65 | + * @param array $def_fields list of default pre-checkin labels and attributes. | |
| 66 | + * | |
| 67 | + * @return array the list of pax fields to collect in the front-end during pre-checkin. | |
| 68 | + * | |
| 69 | + * @since 1.17.2 (J) - 1.7.2 (WP) | |
| 70 | + */ | |
| 71 | + public static function getPrecheckinFields(string $type = '', array $def_fields = []) | |
| 72 | + { | |
| 73 | + // default empty containers | |
| 74 | + $labels = []; | |
| 75 | + $attributes = []; | |
| 76 | + | |
| 77 | + if (empty($type)) { | |
| 78 | + return [$labels, $attributes]; | |
| 79 | + } | |
| 80 | + | |
| 81 | + // invoke custom pax fields object | |
| 82 | + $paxf_obj = self::getInstance($type); | |
| 83 | + if (!$paxf_obj) { | |
| 84 | + return [$labels, $attributes]; | |
| 85 | + } | |
| 86 | + | |
| 87 | + return $paxf_obj->listPrecheckinFields($def_fields); | |
| 88 | + } | |
| 89 | + | |
| 90 | + /** | |
| 91 | + * Invokes a callback on the pax data type driver after the pre-checkin information have been stored or updated. | |
| 92 | + * | |
| 93 | + * @param string $type the pax data type representing the driver. | |
| 94 | + * @param array $data the guest registration data stored. | |
| 95 | + * @param array $booking the booking record involved with the guests registration. | |
| 96 | + * @param array $customer optional customer record associated with the booking. | |
| 97 | + * | |
| 98 | + * @return void | |
| 99 | + * | |
| 100 | + * @since 1.17.5 (J) - 1.7.5 (WP) | |
| 101 | + */ | |
| 102 | + public static function callbackPrecheckinDataStored(string $type, array $data, array $booking, array $customer = []) | |
| 103 | + { | |
| 104 | + if (!$type) { | |
| 105 | + // no driver to invoke | |
| 106 | + return; | |
| 107 | + } | |
| 108 | + | |
| 109 | + // invoke custom pax fields object | |
| 110 | + $paxf_obj = self::getInstance($type); | |
| 111 | + if (!$paxf_obj) { | |
| 112 | + // requested driver not found | |
| 113 | + return; | |
| 114 | + } | |
| 115 | + | |
| 116 | + // let the driver finalize the operation | |
| 117 | + return $paxf_obj->onPrecheckinDataStored($data, $booking, $customer); | |
| 118 | + } | |
| 119 | + | |
| 120 | + /** | |
| 121 | + * Returns the instance of the custom pax data collection type object. | |
| 122 | + * | |
| 123 | + * @param string $type the pax data type representing the class. | |
| 124 | + * | |
| 125 | + * @return null|object the requested driver object or null. | |
| 126 | + */ | |
| 127 | + public static function getInstance($type = '') | |
| 128 | + { | |
| 129 | + // make sure the type of pax data collection type is set | |
| 130 | + if (empty($type)) { | |
| 131 | + if (count(static::$instances)) { | |
| 132 | + // we return the last previously instantiated object | |
| 133 | + $type = key(static::$instances); | |
| 134 | + return static::$instances[$type]; | |
| 135 | + } | |
| 136 | + // get the currently active pax data collection type | |
| 137 | + $type = VBOFactory::getConfig()->getString('checkindata', 'basic'); | |
| 138 | + } | |
| 139 | + | |
| 140 | + if (isset(static::$instances[$type])) { | |
| 141 | + return static::$instances[$type]; | |
| 142 | + } | |
| 143 | + | |
| 144 | + // invoke custom pax fields class | |
| 145 | + $custom_paxf_class = self::getPaxClass($type); | |
| 146 | + | |
| 147 | + if (!class_exists($custom_paxf_class)) { | |
| 148 | + return null; | |
| 149 | + } | |
| 150 | + | |
| 151 | + // invoke and cache the object | |
| 152 | + static::$instances[$type] = new $custom_paxf_class(); | |
| 153 | + | |
| 154 | + // register the data container registry for this collection type | |
| 155 | + static::$instances_data[$type] = new JObject; | |
| 156 | + | |
| 157 | + // return the object | |
| 158 | + return static::$instances[$type]; | |
| 159 | + } | |
| 160 | + | |
| 161 | + /** | |
| 162 | + * Attempts to get the instance data registry for the data collection type previously | |
| 163 | + * previously loaded. This serves to push and cache heavy data so that it will be read | |
| 164 | + * only once, in case multiple pax fields will need to access such values multiple times. | |
| 165 | + * | |
| 166 | + * @return null|JObject the driver object instance data registry, or null. | |
| 167 | + */ | |
| 168 | + public static function getInstanceData() | |
| 169 | + { | |
| 170 | + if (!count(static::$instances)) { | |
| 171 | + // no drivers ever loaded | |
| 172 | + return null; | |
| 173 | + } | |
| 174 | + | |
| 175 | + // get the first (previous) type of collection driver loaded | |
| 176 | + $type = key(static::$instances); | |
| 177 | + | |
| 178 | + return isset(static::$instances_data[$type]) ? static::$instances_data[$type] : null; | |
| 179 | + } | |
| 180 | + | |
| 181 | + /** | |
| 182 | + * Returns a list of all custom drivers available. | |
| 183 | + * | |
| 184 | + * @param bool $no_basic whether to exclude the "basic" (default) driver. | |
| 185 | + * | |
| 186 | + * @return array the associative list of custom pax data drivers. | |
| 187 | + */ | |
| 188 | + public static function getDrivers($no_basic = true) | |
| 189 | + { | |
| 190 | + $drivers_list = []; | |
| 191 | + $drivers_base = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'paxfields' . DIRECTORY_SEPARATOR; | |
| 192 | + $drivers_files = glob($drivers_base . '*.php'); | |
| 193 | + | |
| 194 | + /** | |
| 195 | + * Trigger event to let other plugins register additional drivers. | |
| 196 | + * | |
| 197 | + * @return array a list of supported drivers. | |
| 198 | + */ | |
| 199 | + $list = VBOFactory::getPlatform()->getDispatcher()->filter('onLoadPaxdatafieldsDrivers'); | |
| 200 | + foreach ($list as $chunk) { | |
| 201 | + // merge default driver files with the returned ones | |
| 202 | + $drivers_files = array_merge($drivers_files, (array)$chunk); | |
| 203 | + } | |
| 204 | + | |
| 205 | + foreach ($drivers_files as $df) { | |
| 206 | + $driver_base_name = basename($df, '.php'); | |
| 207 | + if ($no_basic && !strcasecmp($driver_base_name, 'basic')) { | |
| 208 | + continue; | |
| 209 | + } | |
| 210 | + $drivers_list[] = $driver_base_name; | |
| 211 | + } | |
| 212 | + | |
| 213 | + return $drivers_list; | |
| 214 | + } | |
| 215 | + | |
| 216 | + /** | |
| 217 | + * Helper method to get the decoded pax_data of an existing booking. | |
| 218 | + * | |
| 219 | + * @param int $bid the booking ID. | |
| 220 | + * | |
| 221 | + * @return null|array the decoded pax data or null. | |
| 222 | + */ | |
| 223 | + public static function getBookingPaxData($bid) | |
| 224 | + { | |
| 225 | + if (empty($bid)) { | |
| 226 | + return null; | |
| 227 | + } | |
| 228 | + | |
| 229 | + $dbo = JFactory::getDbo(); | |
| 230 | + | |
| 231 | + $q = "SELECT `pax_data` FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . (int)$bid; | |
| 232 | + $dbo->setQuery($q, 0, 1); | |
| 233 | + $dbo->execute(); | |
| 234 | + if (!$dbo->getNumRows()) { | |
| 235 | + return null; | |
| 236 | + } | |
| 237 | + $pax_data = $dbo->loadResult(); | |
| 238 | + if (empty($pax_data)) { | |
| 239 | + return null; | |
| 240 | + } | |
| 241 | + | |
| 242 | + // attempt to decode current data | |
| 243 | + $pax_data = json_decode($pax_data, true); | |
| 244 | + | |
| 245 | + return is_array($pax_data) && count($pax_data) ? $pax_data : null; | |
| 246 | + } | |
| 247 | + | |
| 248 | + /** | |
| 249 | + * Helper method to update the pax_data of an existing booking. | |
| 250 | + * | |
| 251 | + * @param int $bid the booking ID. | |
| 252 | + * @param array $pax_data the array of room guests pax data. | |
| 253 | + * | |
| 254 | + * @return bool true if record was updated. | |
| 255 | + */ | |
| 256 | + public static function setBookingPaxData($bid, $pax_data) | |
| 257 | + { | |
| 258 | + if (empty($bid) || !is_array($pax_data) || !count($pax_data)) { | |
| 259 | + return false; | |
| 260 | + } | |
| 261 | + | |
| 262 | + $dbo = JFactory::getDbo(); | |
| 263 | + | |
| 264 | + $record = new stdClass; | |
| 265 | + $record->idorder = $bid; | |
| 266 | + $record->pax_data = json_encode($pax_data); | |
| 267 | + | |
| 268 | + return $dbo->updateObject('#__vikbooking_customers_orders', $record, 'idorder'); | |
| 269 | + } | |
| 270 | + | |
| 271 | + /** | |
| 272 | + * Helper method to load the previous pax data for this customer in case | |
| 273 | + * previous check-ins were made already for this customer. This is to | |
| 274 | + * quicky populate the previous check-in information. | |
| 275 | + * | |
| 276 | + * @param int $bid the booking ID. | |
| 277 | + * @param int $lim the maximum number of previous records to load. | |
| 278 | + * | |
| 279 | + * @return array the list of previous pax data records for this customer. | |
| 280 | + * | |
| 281 | + * @since 1.16.0 (J) - 1.6.0 (WP) | |
| 282 | + */ | |
| 283 | + public static function getCustomerAllPaxData($bid, $lim = 5) | |
| 284 | + { | |
| 285 | + if (empty($bid)) { | |
| 286 | + return []; | |
| 287 | + } | |
| 288 | + | |
| 289 | + $dbo = JFactory::getDbo(); | |
| 290 | + | |
| 291 | + $q = "SELECT `idcustomer` FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . (int)$bid; | |
| 292 | + $dbo->setQuery($q, 0, 1); | |
| 293 | + $id_customer = $dbo->loadResult(); | |
| 294 | + if (!$id_customer) { | |
| 295 | + return []; | |
| 296 | + } | |
| 297 | + | |
| 298 | + $q = "SELECT `co`.`idorder`, `co`.`pax_data`, `o`.`ts`, `o`.`checkin`, `o`.`checkout` FROM `#__vikbooking_customers_orders` AS `co` | |
| 299 | + LEFT JOIN `#__vikbooking_orders` AS `o` ON `co`.`idorder`=`o`.`id` | |
| 300 | + WHERE `co`.`idcustomer`=" . (int)$id_customer . " AND `co`.`idorder` != " . (int)$bid . " AND `co`.`pax_data` IS NOT NULL ORDER BY `co`.`idorder` DESC"; | |
| 301 | + $dbo->setQuery($q, 0, $lim); | |
| 302 | + $prev_records = $dbo->loadAssocList(); | |
| 303 | + if (!$prev_records) { | |
| 304 | + return []; | |
| 305 | + } | |
| 306 | + | |
| 307 | + foreach ($prev_records as $k => $v) { | |
| 308 | + // attempt to decode checkin data | |
| 309 | + $pax_data = json_decode($v['pax_data'], true); | |
| 310 | + if (!is_array($pax_data) || !count($pax_data)) { | |
| 311 | + unset($prev_records[$k]); | |
| 312 | + continue; | |
| 313 | + } | |
| 314 | + $prev_records[$k]['pax_data'] = $pax_data; | |
| 315 | + } | |
| 316 | + | |
| 317 | + return $prev_records; | |
| 318 | + } | |
| 319 | + | |
| 320 | + /** | |
| 321 | + * Given a type string, returns the full class name for this driver. | |
| 322 | + * | |
| 323 | + * @param string $type the pax data type representing the class. | |
| 324 | + * | |
| 325 | + * @return string the name of the class for the custom pax data collection. | |
| 326 | + */ | |
| 327 | + private static function getPaxClass($type) | |
| 328 | + { | |
| 329 | + $base_paxf_class = 'VBOCheckinPaxfields'; | |
| 330 | + | |
| 331 | + $type_class = ucwords(str_replace(array('-', '_'), ' ', $type)); | |
| 332 | + $type_class = preg_replace("/[^a-zA-Z0-9]/", '', $type_class); | |
| 333 | + | |
| 334 | + return $base_paxf_class . $type_class; | |
| 335 | + } | |
| 336 | +} | |