| 1 |
<?php |
| 2 |
/** |
| 3 |
* Single location class. |
| 4 |
* |
| 5 |
* @package Orderable/Classes |
| 6 |
*/ |
| 7 |
|
| 8 |
defined( 'ABSPATH' ) || exit; |
| 9 |
|
| 10 |
/** |
| 11 |
* Single location class. |
| 12 |
*/ |
| 13 |
class Orderable_Location_Single { |
| 14 |
/** |
| 15 |
* Location data. |
| 16 |
* |
| 17 |
* @var array |
| 18 |
*/ |
| 19 |
public $location_data = array( |
| 20 |
'location_id' => 0, |
| 21 |
'override_default_open_hours' => false, |
| 22 |
'asap_date' => '', |
| 23 |
'asap_time' => '', |
| 24 |
'address_line_1' => '', |
| 25 |
'address_line_2' => '', |
| 26 |
'city' => '', |
| 27 |
'country_state' => '', |
| 28 |
'postcode_zip' => '', |
| 29 |
'open_hours' => array(), |
| 30 |
'enable_default_holidays' => true, |
| 31 |
'pickup_hours_same_as_delivery' => true, |
| 32 |
); |
| 33 |
|
| 34 |
/** |
| 35 |
* Constructor. |
| 36 |
* |
| 37 |
* @param int|array|null $location Location ID or row data. |
| 38 |
*/ |
| 39 |
public function __construct( $location = null ) { |
| 40 |
$location_data = array(); |
| 41 |
|
| 42 |
// Get selected location ID if none is passed in. |
| 43 |
if ( empty( $location ) ) { |
| 44 |
$location = Orderable_Location::get_selected_location_id(); |
| 45 |
} |
| 46 |
|
| 47 |
// Get the main location data. |
| 48 |
if ( empty( $location ) ) { |
| 49 |
$location_data['open_hours'] = Orderable_Location::get_default_open_hours(); |
| 50 |
} else { |
| 51 |
$location_data = is_numeric( $location ) ? Orderable_Location::get_location_data( $location ) : $location; |
| 52 |
} |
| 53 |
|
| 54 |
$this->location_data = wp_parse_args( $location_data, $this->location_data ); |
| 55 |
|
| 56 |
/** |
| 57 |
* Action to run after the location object is initialized. |
| 58 |
* |
| 59 |
* @since 1.13.0 |
| 60 |
*/ |
| 61 |
do_action( 'orderable_location_object_init', $this ); |
| 62 |
} |
| 63 |
|
| 64 |
/** |
| 65 |
* Get location ID. |
| 66 |
* |
| 67 |
* @return int |
| 68 |
*/ |
| 69 |
public function get_location_id() { |
| 70 |
return absint( $this->location_data['location_id'] ); |
| 71 |
} |
| 72 |
|
| 73 |
/** |
| 74 |
* Get location name. |
| 75 |
* |
| 76 |
* @return int |
| 77 |
*/ |
| 78 |
public function get_title() { |
| 79 |
return $this->location_data['title']; |
| 80 |
} |
| 81 |
|
| 82 |
/** |
| 83 |
* Is service enabled? |
| 84 |
* |
| 85 |
* @param string $service_type Service type (delivery/pickup). |
| 86 |
* |
| 87 |
* @return bool |
| 88 |
*/ |
| 89 |
public function is_service_enabled( $service_type ) { |
| 90 |
return ! empty( $this->location_data[ $service_type ] ); |
| 91 |
} |
| 92 |
|
| 93 |
/** |
| 94 |
* Get services. |
| 95 |
* |
| 96 |
* @return array |
| 97 |
*/ |
| 98 |
public function get_services() { |
| 99 |
$services = array(); |
| 100 |
|
| 101 |
if ( $this->is_service_enabled( 'delivery' ) ) { |
| 102 |
$services[] = 'delivery'; |
| 103 |
} |
| 104 |
|
| 105 |
if ( $this->is_service_enabled( 'pickup' ) ) { |
| 106 |
$services[] = 'pickup'; |
| 107 |
} |
| 108 |
|
| 109 |
return $services; |
| 110 |
} |
| 111 |
|
| 112 |
/** |
| 113 |
* Does the location have any services enabled. |
| 114 |
* |
| 115 |
* @return bool |
| 116 |
*/ |
| 117 |
public function has_services() { |
| 118 |
return ! empty( $this->get_services() ); |
| 119 |
} |
| 120 |
|
| 121 |
/** |
| 122 |
* Get lead time period. |
| 123 |
* |
| 124 |
* @return string |
| 125 |
*/ |
| 126 |
public function get_lead_time_period() { |
| 127 |
return ! empty( $this->location_data['lead_time_period'] ) ? $this->location_data['lead_time_period'] : 'days'; |
| 128 |
} |
| 129 |
|
| 130 |
/** |
| 131 |
* Get lead time. |
| 132 |
* |
| 133 |
* @param bool $in_seconds In seconds. |
| 134 |
* |
| 135 |
* @return int |
| 136 |
*/ |
| 137 |
public function get_lead_time( $in_seconds = false ) { |
| 138 |
$lead_time = ! empty( $this->location_data['lead_time'] ) ? absint( $this->location_data['lead_time'] ) : 0; |
| 139 |
|
| 140 |
if ( 0 === $lead_time || ! $in_seconds ) { |
| 141 |
/** |
| 142 |
* Filter to modify the lead time. |
| 143 |
* |
| 144 |
* @since 1.9.0 |
| 145 |
*/ |
| 146 |
return apply_filters( 'orderable_location_get_lead_time', $lead_time ); |
| 147 |
} |
| 148 |
|
| 149 |
$lead_time_period = $this->get_lead_time_period(); |
| 150 |
|
| 151 |
if ( 'days' === $lead_time_period ) { |
| 152 |
$lead_time *= 86400; |
| 153 |
} elseif ( 'hours' === $lead_time_period ) { |
| 154 |
$lead_time *= 3600; |
| 155 |
} elseif ( 'minutes' === $lead_time_period ) { |
| 156 |
$lead_time *= 60; |
| 157 |
} |
| 158 |
|
| 159 |
/** |
| 160 |
* Filter to modify the lead time. |
| 161 |
* |
| 162 |
* @since 1.9.0 |
| 163 |
*/ |
| 164 |
return apply_filters( 'orderable_location_get_lead_time', $lead_time ); |
| 165 |
} |
| 166 |
|
| 167 |
/** |
| 168 |
* Get preorder days. |
| 169 |
* |
| 170 |
* @return int |
| 171 |
*/ |
| 172 |
public function get_preorder_days() { |
| 173 |
return isset( $this->location_data['preorder'] ) ? absint( $this->location_data['preorder'] ) : 7; |
| 174 |
} |
| 175 |
|
| 176 |
/** |
| 177 |
* Get delivery days calculation method. |
| 178 |
* |
| 179 |
* @return string |
| 180 |
*/ |
| 181 |
public function get_delivery_calculation_method() { |
| 182 |
return ! empty( $this->location_data['delivery_days_calculation_method'] ) ? $this->location_data['delivery_days_calculation_method'] : 'all'; |
| 183 |
} |
| 184 |
|
| 185 |
/** |
| 186 |
* Get override default open hours setting. |
| 187 |
* |
| 188 |
* @return bool |
| 189 |
*/ |
| 190 |
public function get_override_default_open_hours() { |
| 191 |
return ! in_array( $this->location_data['override_default_open_hours'], array( false, '0' ), true ); |
| 192 |
} |
| 193 |
|
| 194 |
/** |
| 195 |
* Get enable default holidays setting. |
| 196 |
* |
| 197 |
* @return bool |
| 198 |
*/ |
| 199 |
public function get_enable_default_holidays() { |
| 200 |
return ! in_array( $this->location_data['enable_default_holidays'], array( false, '0' ), true ); |
| 201 |
} |
| 202 |
|
| 203 |
/** |
| 204 |
* Get pickup hours same as delivery setting. |
| 205 |
* |
| 206 |
* @return bool |
| 207 |
*/ |
| 208 |
public function get_pickup_hours_same_as_delivery() { |
| 209 |
return ! in_array( $this->location_data['pickup_hours_same_as_delivery'], array( false, '0' ), true ); |
| 210 |
} |
| 211 |
|
| 212 |
/** |
| 213 |
* Get ASAP settings |
| 214 |
* |
| 215 |
* @return array |
| 216 |
*/ |
| 217 |
public function get_asap_settings() { |
| 218 |
return array( |
| 219 |
'date' => '1' === $this->location_data['asap_date'], |
| 220 |
'time' => '1' === $this->location_data['asap_time'], |
| 221 |
); |
| 222 |
} |
| 223 |
|
| 224 |
/** |
| 225 |
* Get service days. |
| 226 |
* |
| 227 |
* @param string $service_type Service type (delivery/pickup). |
| 228 |
* |
| 229 |
* @return array |
| 230 |
*/ |
| 231 |
public function get_service_days( $service_type = 'delivery' ) { |
| 232 |
$days = Orderable_Timings::get_days_of_the_week(); |
| 233 |
|
| 234 |
$settings = $this->get_service_hours( $service_type, false, true ); |
| 235 |
$service_days = array(); |
| 236 |
|
| 237 |
if ( empty( $settings ) ) { |
| 238 |
return $service_days; |
| 239 |
} |
| 240 |
|
| 241 |
foreach ( $settings as $setting_row ) { |
| 242 |
if ( empty( $setting_row['days'] ) ) { |
| 243 |
continue; |
| 244 |
} |
| 245 |
|
| 246 |
foreach ( $setting_row['days'] as $day_number ) { |
| 247 |
$service_days[ $day_number ] = $days[ $day_number ]; |
| 248 |
} |
| 249 |
} |
| 250 |
|
| 251 |
return $service_days; |
| 252 |
} |
| 253 |
|
| 254 |
/** |
| 255 |
* Get service hours. |
| 256 |
* |
| 257 |
* @param null $service_type Service type (delivery/pickup). |
| 258 |
* @param bool $is_admin Is this an admin request? If so, collect all data for location. |
| 259 |
* @param bool $skip_zone Skip the zone ID. |
| 260 |
* |
| 261 |
* @return array |
| 262 |
*/ |
| 263 |
public function get_service_hours( $service_type = null, $is_admin = false, $skip_zone = false ) { |
| 264 |
$zone_id = Orderable_Location_Zones::get_selected_shipping_zone_id(); |
| 265 |
|
| 266 |
if ( false === $zone_id && ! $is_admin ) { |
| 267 |
/** |
| 268 |
* Filter to modify the service hours. |
| 269 |
* |
| 270 |
* @param array $service_hours The service hours. |
| 271 |
* @param Orderable_Location_Single $location Current location object. |
| 272 |
* @param string|null $service_type The service type. |
| 273 |
* @param bool $is_admin Is this an admin request? |
| 274 |
* @param bool $skip_zone Skip the zone ID. |
| 275 |
* |
| 276 |
* @since 1.14.0 |
| 277 |
*/ |
| 278 |
return apply_filters( 'orderable_get_service_hours', array(), $this, $service_type, $is_admin, $skip_zone ); |
| 279 |
} |
| 280 |
|
| 281 |
// Switch service type to 'delivery' if pickup hours are the same as delivery. |
| 282 |
$original_service_type = $service_type; |
| 283 |
$service_type = ! $is_admin && 'pickup' === $service_type && $this->get_pickup_hours_same_as_delivery() ? 'delivery' : $service_type; |
| 284 |
|
| 285 |
$location_id = $this->get_location_id(); |
| 286 |
$cache_key = "orderable_time_slots_{$location_id}"; |
| 287 |
|
| 288 |
if ( $original_service_type ) { |
| 289 |
$cache_key .= "_{$original_service_type}"; |
| 290 |
} |
| 291 |
|
| 292 |
if ( ! $skip_zone && false !== $zone_id ) { |
| 293 |
$cache_key .= "_{$zone_id}"; |
| 294 |
} |
| 295 |
|
| 296 |
$cached_service_hours = wp_cache_get( $cache_key ); |
| 297 |
|
| 298 |
if ( false !== $cached_service_hours ) { |
| 299 |
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment |
| 300 |
return apply_filters( 'orderable_get_service_hours', $cached_service_hours, $this, $service_type, $is_admin, $skip_zone ); |
| 301 |
} |
| 302 |
|
| 303 |
global $wpdb; |
| 304 |
|
| 305 |
$query = "SELECT DISTINCT |
| 306 |
ts.* |
| 307 |
FROM |
| 308 |
{$wpdb->prefix}orderable_location_time_slots ts |
| 309 |
LEFT JOIN |
| 310 |
{$wpdb->prefix}orderable_location_delivery_zones_lookup l |
| 311 |
ON ts.location_id = l.location_id AND ts.time_slot_id = l.time_slot_id |
| 312 |
WHERE |
| 313 |
ts.location_id = %d"; |
| 314 |
|
| 315 |
$query_params = array( |
| 316 |
$location_id, |
| 317 |
); |
| 318 |
|
| 319 |
if ( $service_type ) { |
| 320 |
$query .= ' AND ts.service_type = %s'; |
| 321 |
$query_params[] = $service_type; |
| 322 |
} |
| 323 |
|
| 324 |
// Zone doesn't matter for pickup. |
| 325 |
if ( 'pickup' !== $original_service_type && ! empty( $zone_id ) && ! $skip_zone ) { |
| 326 |
$query .= ' AND (l.zone_id = %d OR ts.has_zones = 0)'; |
| 327 |
$query_params[] = $zone_id; |
| 328 |
} |
| 329 |
|
| 330 |
$service_hours = $wpdb->get_results( |
| 331 |
$wpdb->prepare( |
| 332 |
$query, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared |
| 333 |
$query_params |
| 334 |
), |
| 335 |
ARRAY_A |
| 336 |
); |
| 337 |
|
| 338 |
if ( empty( $service_hours ) ) { |
| 339 |
$service_hours = array(); |
| 340 |
} else { |
| 341 |
foreach ( $service_hours as &$service_hour ) { |
| 342 |
$service_hour['days'] = (array) maybe_unserialize( $service_hour['days'] ); |
| 343 |
$service_hour['from'] = maybe_unserialize( $service_hour['time_from'] ); |
| 344 |
$service_hour['to'] = maybe_unserialize( $service_hour['time_to'] ); |
| 345 |
} |
| 346 |
} |
| 347 |
|
| 348 |
wp_cache_set( $cache_key, $service_hours, '', ORDERABLE_CACHE_EXPIRATION_TIME ); |
| 349 |
|
| 350 |
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment |
| 351 |
return apply_filters( 'orderable_get_service_hours', $service_hours, $this, $service_type, $is_admin, $skip_zone ); |
| 352 |
} |
| 353 |
|
| 354 |
/** |
| 355 |
* Get services on day. |
| 356 |
* |
| 357 |
* @param int $timestamp Timestamp of specific day at 00:00am (GMT). |
| 358 |
* |
| 359 |
* @return array |
| 360 |
*/ |
| 361 |
public function get_services_on_day( $timestamp ) { |
| 362 |
$timestamp_adjusted = Orderable_Timings::get_timestamp_adjusted( $timestamp ); |
| 363 |
$services_on_day = array(); |
| 364 |
$services = $this->get_services(); |
| 365 |
|
| 366 |
if ( empty( $services ) ) { |
| 367 |
return $services_on_day; |
| 368 |
} |
| 369 |
|
| 370 |
$day_to_check = absint( date( 'w', $timestamp_adjusted ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date |
| 371 |
|
| 372 |
foreach ( $services as $service ) { |
| 373 |
$service_days = $this->get_service_days( $service ); |
| 374 |
$services_on_day[ $service ] = isset( $service_days[ $day_to_check ] ) && ! $this->is_holiday( $timestamp, $service ); |
| 375 |
} |
| 376 |
|
| 377 |
return $services_on_day; |
| 378 |
} |
| 379 |
|
| 380 |
/** |
| 381 |
* Get open days. |
| 382 |
* |
| 383 |
* @return array |
| 384 |
*/ |
| 385 |
public function get_open_days() { |
| 386 |
static $open_days_cache; |
| 387 |
|
| 388 |
if ( ! empty( $open_days_cache[ $this->get_location_id() ] ) ) { |
| 389 |
// phpcs:ignore WooCommerce.Commenting.CommentHooks |
| 390 |
return apply_filters( 'orderable_location_get_open_days', $open_days_cache[ $this->get_location_id() ], $this ); |
| 391 |
} |
| 392 |
|
| 393 |
$open_days = array(); |
| 394 |
$open_hours_settings = $this->get_open_hours(); |
| 395 |
$days_of_the_week = Orderable_Timings::get_days_of_the_week(); |
| 396 |
|
| 397 |
foreach ( $open_hours_settings as $day => $open_hour ) { |
| 398 |
if ( ! empty( $open_hour['enabled'] ) ) { |
| 399 |
$open_days[ $day ] = $days_of_the_week[ $day ]; |
| 400 |
} |
| 401 |
} |
| 402 |
|
| 403 |
/** |
| 404 |
* Filter location open days. |
| 405 |
* |
| 406 |
* @since 1.8.0 |
| 407 |
* @hook orderable_location_get_open_days |
| 408 |
* @param array $open_days Location open days. |
| 409 |
* @param Orderable_Location_Single $location Location object. |
| 410 |
*/ |
| 411 |
$open_days = apply_filters( 'orderable_location_get_open_days', $open_days, $this ); |
| 412 |
|
| 413 |
$open_days_cache[ $this->get_location_id() ] = $open_days; |
| 414 |
|
| 415 |
return $open_days; |
| 416 |
} |
| 417 |
|
| 418 |
/** |
| 419 |
* Is the location open? |
| 420 |
* |
| 421 |
* @param int $timestamp Timestamp (GMT). |
| 422 |
* |
| 423 |
* @return bool |
| 424 |
*/ |
| 425 |
public function is_open( $timestamp ) { |
| 426 |
$date_time = Orderable_Timings::get_date_time_by_timestamp( $timestamp ); |
| 427 |
$open_days = $this->get_open_days(); |
| 428 |
|
| 429 |
return array_key_exists( $date_time->format( 'w' ), $open_days ); |
| 430 |
} |
| 431 |
|
| 432 |
/** |
| 433 |
* Get open hours. |
| 434 |
* |
| 435 |
* @return array |
| 436 |
*/ |
| 437 |
public function get_open_hours() { |
| 438 |
static $open_hours_cache; |
| 439 |
|
| 440 |
if ( ! empty( $open_hours_cache[ $this->get_location_id() ] ) ) { |
| 441 |
// phpcs:ignore WooCommerce.Commenting.CommentHooks |
| 442 |
return apply_filters( 'orderable_location_get_open_hours', $open_hours_cache[ $this->get_location_id() ], $this ); |
| 443 |
} |
| 444 |
|
| 445 |
if ( $this->get_override_default_open_hours() ) { |
| 446 |
$open_hours = maybe_unserialize( $this->location_data['open_hours'] ); |
| 447 |
$open_hours = ! empty( $open_hours ) ? $open_hours : array(); |
| 448 |
} else { |
| 449 |
$open_hours = Orderable_Location::get_default_open_hours(); |
| 450 |
} |
| 451 |
|
| 452 |
/** |
| 453 |
* Filter location open hours. |
| 454 |
* |
| 455 |
* @since 1.8.0 |
| 456 |
* @hook orderable_location_get_open_hours |
| 457 |
* @param array $open_hours Location open hours. |
| 458 |
* @param Orderable_Location_Single $location Location object. |
| 459 |
*/ |
| 460 |
$open_hours = apply_filters( 'orderable_location_get_open_hours', $open_hours, $this ); |
| 461 |
|
| 462 |
$open_hours_cache[ $this->get_location_id() ] = $open_hours; |
| 463 |
|
| 464 |
return $open_hours; |
| 465 |
} |
| 466 |
|
| 467 |
/** |
| 468 |
* Get upcoming open hours. |
| 469 |
* |
| 470 |
* @return array |
| 471 |
*/ |
| 472 |
public function get_upcoming_open_hours() { |
| 473 |
$open_hours = array(); |
| 474 |
$days = Orderable_Timings::get_days_of_the_week( 'full', 0 ); |
| 475 |
$open_hours_settings = $this->get_open_hours(); |
| 476 |
$current_day = absint( current_time( 'w', true ) ); |
| 477 |
$tense = 'last'; |
| 478 |
|
| 479 |
foreach ( $days as $index => $day ) { |
| 480 |
$day_settings = isset( $open_hours_settings[ $index ] ) ? $open_hours_settings[ $index ] : null; |
| 481 |
|
| 482 |
if ( empty( $day_settings ) ) { |
| 483 |
continue; |
| 484 |
} |
| 485 |
|
| 486 |
if ( $index === $current_day ) { |
| 487 |
$tense = 'this'; |
| 488 |
} |
| 489 |
|
| 490 |
$day_name_en = date( 'l', strtotime( "Sunday +{$index} days" ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date |
| 491 |
$datetime = new DateTime( $tense . ' ' . $day_name_en, wp_timezone() ); |
| 492 |
$timestamp = $datetime->getTimestamp(); |
| 493 |
$timestamp_adjusted = Orderable_Timings::get_timestamp_adjusted( $timestamp ); |
| 494 |
$services_on_day = $this->get_services_on_day( $timestamp ); |
| 495 |
$hours = __( 'Closed', 'orderable' ); |
| 496 |
$is_holiday = $this->is_holiday( $timestamp ); |
| 497 |
$is_holiday = $is_holiday && empty( array_filter( $services_on_day ) ); |
| 498 |
$open = ! empty( $day_settings['enabled'] ) && $day_settings['enabled']; |
| 499 |
|
| 500 |
if ( $open && $is_holiday ) { |
| 501 |
$hours = __( 'Holiday', 'orderable' ); |
| 502 |
} elseif ( $open && ! $is_holiday ) { |
| 503 |
$from = sprintf( '%s:%s %s', $day_settings['from']['hour'], $day_settings['from']['minute'], $day_settings['from']['period'] ); |
| 504 |
$to = sprintf( '%s:%s %s', $day_settings['to']['hour'], $day_settings['to']['minute'], $day_settings['to']['period'] ); |
| 505 |
$hours = sprintf( '%s — %s', $from, $to ); |
| 506 |
} |
| 507 |
|
| 508 |
$open_hours[ $index ] = array( |
| 509 |
'day' => $day, |
| 510 |
'date' => date( 'd', $timestamp_adjusted ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date |
| 511 |
'hours' => $hours, |
| 512 |
'is_closed' => ! $open, |
| 513 |
'services' => $services_on_day, |
| 514 |
); |
| 515 |
} |
| 516 |
|
| 517 |
/** |
| 518 |
* Filter location upcoming open hours. |
| 519 |
* |
| 520 |
* @since 1.8.0 |
| 521 |
* @hook orderable_upcoming_open_hours |
| 522 |
* @param array $open_hours Location upcoming open hours. |
| 523 |
* @param Orderable_Location_Single $location Location object. |
| 524 |
*/ |
| 525 |
return apply_filters( 'orderable_upcoming_open_hours', $open_hours, $this ); |
| 526 |
} |
| 527 |
|
| 528 |
/** |
| 529 |
* Get slots for service type and day of the week. |
| 530 |
* |
| 531 |
* @param int $timestamp Timestamp (GMT). |
| 532 |
* @param string $type Service type (delivery|pickup). |
| 533 |
* |
| 534 |
* @return array |
| 535 |
*/ |
| 536 |
public function get_slots( $timestamp, $type = 'delivery' ) { |
| 537 |
$slots = array(); |
| 538 |
|
| 539 |
if ( empty( $timestamp ) ) { |
| 540 |
return $slots; |
| 541 |
} |
| 542 |
|
| 543 |
$settings = $this->get_service_hours( $type ); |
| 544 |
|
| 545 |
if ( empty( $settings ) ) { |
| 546 |
return $slots; |
| 547 |
} |
| 548 |
|
| 549 |
$date_time = Orderable_Timings::get_date_time_by_timestamp( $timestamp ); |
| 550 |
$current_timestamp = time(); |
| 551 |
$timestamp = ! $timestamp ? $current_timestamp : $timestamp; |
| 552 |
$day_number = (int) $date_time->format( 'w' ); // 0 (Sunday) through 6 (Saturday). |
| 553 |
|
| 554 |
foreach ( $settings as $setting_key => $setting_row ) { |
| 555 |
$days = array_map( 'absint', $setting_row['days'] ); |
| 556 |
|
| 557 |
if ( ! in_array( $day_number, $days, true ) ) { |
| 558 |
continue; |
| 559 |
} |
| 560 |
|
| 561 |
$slots = array( |
| 562 |
'all-day' => array( |
| 563 |
'formatted' => __( 'All Day', 'orderable' ), |
| 564 |
'value' => 'all-day', |
| 565 |
'timestamp' => $timestamp, |
| 566 |
'setting_key' => $setting_key, |
| 567 |
'setting_row' => $setting_row, |
| 568 |
), |
| 569 |
); |
| 570 |
|
| 571 |
break; |
| 572 |
} |
| 573 |
|
| 574 |
if ( has_filter( 'orderable_get_slots' ) ) { |
| 575 |
_deprecated_hook( 'orderable_get_slots', '1.8.0', 'orderable_location_get_slots' ); |
| 576 |
|
| 577 |
/** |
| 578 |
* Filter location slots. |
| 579 |
* |
| 580 |
* @since 1.0.0 |
| 581 |
* @hook orderable_get_slots |
| 582 |
* @deprecated 1.8.0 Use orderable_location_get_slots instead. |
| 583 |
* |
| 584 |
* @param array $slots Location slots. |
| 585 |
* @param int $timestamp Timestamp (GMT). |
| 586 |
* @param string $type The service type. Either 'delivery' or 'pickup'. |
| 587 |
* @param Orderable_Location_Single $location Location object. |
| 588 |
*/ |
| 589 |
$slots = apply_filters( 'orderable_get_slots', $slots, $timestamp, $type, $this ); |
| 590 |
} |
| 591 |
|
| 592 |
/** |
| 593 |
* Filter location slots. |
| 594 |
* |
| 595 |
* @since 1.8.0 |
| 596 |
* @hook orderable_location_get_slots |
| 597 |
* |
| 598 |
* @param array $slots Location slots. |
| 599 |
* @param int $timestamp Timestamp (GMT). |
| 600 |
* @param string $type The service type. Either 'delivery' or 'pickup'. |
| 601 |
* @param Orderable_Location_Single $location Location object. |
| 602 |
*/ |
| 603 |
return apply_filters( 'orderable_location_get_slots', $slots, $timestamp, $type, $this ); |
| 604 |
} |
| 605 |
|
| 606 |
/** |
| 607 |
* Get holidays. |
| 608 |
* |
| 609 |
* @param null $type Service type (delivery/pickup). |
| 610 |
* @param bool $include_defaults Include default holidays. |
| 611 |
* |
| 612 |
* @return array |
| 613 |
*/ |
| 614 |
public function get_holidays( $type = null, $include_defaults = true ) { |
| 615 |
static $holidays_cache; |
| 616 |
|
| 617 |
if ( ! empty( $holidays_cache[ $this->get_location_id() ] ) ) { |
| 618 |
// phpcs:ignore WooCommerce.Commenting.CommentHooks |
| 619 |
return apply_filters( 'orderable_location_get_holidays', $holidays_cache[ $this->get_location_id() ], $type, $include_defaults, $this ); |
| 620 |
} |
| 621 |
|
| 622 |
global $wpdb; |
| 623 |
|
| 624 |
$holidays = $include_defaults && $this->get_enable_default_holidays() ? (array) Orderable_Settings::get_setting( 'holidays' ) : array(); |
| 625 |
|
| 626 |
$sql = "SELECT |
| 627 |
holiday_id, |
| 628 |
date_from 'from', |
| 629 |
date_to 'to', |
| 630 |
services, |
| 631 |
repeat_yearly 'repeat' |
| 632 |
FROM |
| 633 |
{$wpdb->orderable_location_holidays} holidays |
| 634 |
INNER JOIN |
| 635 |
{$wpdb->orderable_locations} locations |
| 636 |
ON |
| 637 |
locations.location_id = holidays.location_id |
| 638 |
WHERE |
| 639 |
locations.location_id = %d"; |
| 640 |
|
| 641 |
$holidays_query = $wpdb->get_results( |
| 642 |
$wpdb->prepare( |
| 643 |
$sql, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared |
| 644 |
$this->get_location_id() |
| 645 |
), |
| 646 |
ARRAY_A |
| 647 |
); |
| 648 |
|
| 649 |
/** |
| 650 |
* Filter location holidays query result. |
| 651 |
* |
| 652 |
* @since 1.14.0 |
| 653 |
*/ |
| 654 |
$holidays_query = apply_filters( 'orderable_location_holidays_query_result', $holidays_query, $this ); |
| 655 |
|
| 656 |
$holidays_query = ! empty( $holidays_query ) ? $holidays_query : array(); |
| 657 |
$holidays = array_merge( $holidays, $holidays_query ); |
| 658 |
|
| 659 |
if ( ! empty( $holidays ) ) { |
| 660 |
$today = new DateTime( 'now', wp_timezone() ); |
| 661 |
$today_timestamp = $today->getTimestamp(); |
| 662 |
|
| 663 |
foreach ( $holidays as $index => $holiday ) { |
| 664 |
if ( empty( $holiday['from'] ) ) { |
| 665 |
continue; |
| 666 |
} |
| 667 |
|
| 668 |
$holidays[ $index ]['services'] = maybe_unserialize( $holiday['services'] ); |
| 669 |
$holidays[ $index ]['timestamps'] = array(); |
| 670 |
|
| 671 |
$from = DateTime::createFromFormat( 'Y-m-d H:i:s', $holiday['from'] . ' 00:00:00', wp_timezone() ); |
| 672 |
$to = ! empty( $holiday['to'] ) ? DateTime::createFromFormat( 'Y-m-d H:i:s', $holiday['to'] . ' 00:00:00', wp_timezone() ) : clone $from; |
| 673 |
|
| 674 |
// Add one minute so last slot is included. |
| 675 |
$to->modify( '+1 minute' ); |
| 676 |
|
| 677 |
// Add years to from and to if this holiday repeats and is in the past. |
| 678 |
if ( $today_timestamp > $to->getTimestamp() && ! empty( $holiday['repeat'] ) ) { |
| 679 |
// Calculate how many years have past and append 1 additional. |
| 680 |
$today_datetime = new DateTime(); |
| 681 |
$today_datetime->setTimestamp( $today_timestamp ); |
| 682 |
|
| 683 |
$interval = $today_datetime->diff( $to ); |
| 684 |
$year = intval( $interval->format( '%Y' ) ) + 1; |
| 685 |
|
| 686 |
$from->modify( '+' . $year . ' year' ); |
| 687 |
$to->modify( '+' . $year . ' year' ); |
| 688 |
|
| 689 |
$holidays[ $index ]['from'] = $from->format( 'Y-m-d' ); |
| 690 |
$holidays[ $index ]['to'] = ! empty( $holidays[ $index ]['to'] ) ? $to->format( 'Y-m-d' ) : ''; |
| 691 |
} |
| 692 |
|
| 693 |
$range = new DatePeriod( |
| 694 |
$from, |
| 695 |
new DateInterval( 'P1D' ), // Every 1 day. |
| 696 |
$to |
| 697 |
); |
| 698 |
|
| 699 |
if ( empty( $range ) ) { |
| 700 |
continue; |
| 701 |
} |
| 702 |
|
| 703 |
foreach ( $range as $time ) { |
| 704 |
$holidays[ $index ]['timestamps'][] = $time->getTimestamp(); |
| 705 |
} |
| 706 |
} |
| 707 |
} |
| 708 |
|
| 709 |
/** |
| 710 |
* Filter location holidays. |
| 711 |
* |
| 712 |
* @since 1.8.0 |
| 713 |
* @hook orderable_location_get_holidays |
| 714 |
* @param array $holidays Location holidays. |
| 715 |
* @param string|null $type Service type (delivery/pickup). |
| 716 |
* @param bool $include_defaults Include default holidays. |
| 717 |
* @param Orderable_Location_Single $location Location object. |
| 718 |
*/ |
| 719 |
$holidays = apply_filters( 'orderable_location_get_holidays', $holidays, $type, $include_defaults, $this ); |
| 720 |
|
| 721 |
$holidays_cache[ $this->get_location_id() ] = $holidays; |
| 722 |
|
| 723 |
return $holidays; |
| 724 |
} |
| 725 |
|
| 726 |
/** |
| 727 |
* Is this timestamp a holiday? |
| 728 |
* |
| 729 |
* @param int $timestamp Timestamp (GMT). |
| 730 |
* @param string|null $type Service type (delivery/pickup). |
| 731 |
* |
| 732 |
* @return bool |
| 733 |
*/ |
| 734 |
public function is_holiday( $timestamp, $type = null ) { |
| 735 |
$is_holiday = false; |
| 736 |
$holidays = $this->get_holidays(); |
| 737 |
|
| 738 |
// If no holidays are set, then this isn't a holiday. |
| 739 |
if ( ! empty( $holidays ) ) { |
| 740 |
foreach ( $holidays as $holiday ) { |
| 741 |
// If no timestamps or not assigned to any service, continue. |
| 742 |
if ( empty( $holiday['timestamps'] ) || empty( $holiday['services'] ) ) { |
| 743 |
continue; |
| 744 |
} |
| 745 |
|
| 746 |
// If timestamp isn't a holiday, continue. |
| 747 |
if ( ! in_array( $timestamp, $holiday['timestamps'], true ) ) { |
| 748 |
continue; |
| 749 |
} |
| 750 |
|
| 751 |
// If we want to check a specific delivery type, check that |
| 752 |
// service is assigned to this holiday. Otherwise, continue. |
| 753 |
if ( $type && ! in_array( $type, $holiday['services'], true ) ) { |
| 754 |
continue; |
| 755 |
} |
| 756 |
|
| 757 |
// If we got here, then this is indeed a holiday. |
| 758 |
$is_holiday = true; |
| 759 |
break; |
| 760 |
} |
| 761 |
} |
| 762 |
|
| 763 |
/** |
| 764 |
* Filter if is a holiday. |
| 765 |
* |
| 766 |
* @since 1.8.0 |
| 767 |
* @hook orderable_location_is_holiday |
| 768 |
* @param bool $is_holiday Is this timestamp a holiday? |
| 769 |
* @param int $timestamp Timestamp (GMT). |
| 770 |
* @param string|null $type Service type (delivery/pickup). |
| 771 |
* @param Orderable_Location_Single $location Location object. |
| 772 |
*/ |
| 773 |
return apply_filters( 'orderable_location_is_holiday', $is_holiday, $timestamp, $type, $this ); |
| 774 |
} |
| 775 |
|
| 776 |
/** |
| 777 |
* Get dates available for service type. |
| 778 |
* |
| 779 |
* @param string $type Type. Can be 'delivery' or 'pickup'. |
| 780 |
* @param bool $ignore_needs_shipping Ignore check for WC()->cart->needs_shipping(). |
| 781 |
* |
| 782 |
* @return array|bool Array when dates are available, "true" when no date selection required, "false" when no dates available. |
| 783 |
*/ |
| 784 |
public function get_service_dates( $type = false, $ignore_needs_shipping = false ) { |
| 785 |
$cache_key = 'orderable_service_dates_' . md5( $type . (int) $ignore_needs_shipping . wp_json_encode( WC()->cart ) . $this->get_location_id() ); |
| 786 |
$cached_result = wp_cache_get( $cache_key ); |
| 787 |
|
| 788 |
if ( false !== $cached_result ) { |
| 789 |
return $cached_result; |
| 790 |
} |
| 791 |
|
| 792 |
/** |
| 793 |
* Filter whether ignoring check for WC()->cart->needs_shipping() when |
| 794 |
* getting service dates. |
| 795 |
* |
| 796 |
* @since 1.14.0 |
| 797 |
* @hook orderable_location_service_dates_ignore_needs_shipping |
| 798 |
* @param bool $ignore_needs_shipping Ignore check for WC()->cart->needs_shipping(). Default: false. |
| 799 |
* @param string $service_type It can be 'delivery' or 'pickup'. |
| 800 |
* @return bool New value |
| 801 |
*/ |
| 802 |
$ignore_needs_shipping = apply_filters( 'orderable_location_service_dates_ignore_needs_shipping', $ignore_needs_shipping, $type ); |
| 803 |
|
| 804 |
if ( ! $ignore_needs_shipping && ! WC()->cart->needs_shipping() ) { |
| 805 |
// For backwards compatibility. |
| 806 |
$result = apply_filters_deprecated( 'orderable-service-dates', array( true, $type ), '1.8.0', 'orderable_location_service_dates' ); |
| 807 |
// Return true. No service is required. |
| 808 |
|
| 809 |
/** |
| 810 |
* Filter orderable service dates. |
| 811 |
* |
| 812 |
* @since 1.8.0 |
| 813 |
* @hook orderable_location_service_dates |
| 814 |
* @see Orderable_Location_Single::get_service_dates() |
| 815 |
*/ |
| 816 |
$service_dates = apply_filters( 'orderable_location_service_dates', $result, $type, $this ); |
| 817 |
|
| 818 |
wp_cache_set( $cache_key, $service_dates, '', ORDERABLE_CACHE_EXPIRATION_TIME ); |
| 819 |
|
| 820 |
return $service_dates; |
| 821 |
} |
| 822 |
|
| 823 |
$type = ! $type ? Orderable_Services::get_selected_service( false ) : $type; |
| 824 |
$service_dates = array(); |
| 825 |
|
| 826 |
if ( ! $type ) { |
| 827 |
// For backwards compatibility. |
| 828 |
$result = apply_filters_deprecated( 'orderable-service-dates', array( false, $type ), '1.8.0', 'orderable_location_service_dates' ); |
| 829 |
// Return false. A service should be selected. |
| 830 |
// @todo Check if this should be true when no shipping method is selected yet. |
| 831 |
|
| 832 |
/** |
| 833 |
* Filter orderable service dates. |
| 834 |
* |
| 835 |
* @since 1.8.0 |
| 836 |
* @hook orderable_location_service_dates |
| 837 |
* @see Orderable_Location_Single::get_service_dates() |
| 838 |
*/ |
| 839 |
$service_dates = apply_filters( 'orderable_location_service_dates', $result, $type, $this ); |
| 840 |
|
| 841 |
wp_cache_set( $cache_key, $service_dates, '', ORDERABLE_CACHE_EXPIRATION_TIME ); |
| 842 |
|
| 843 |
return $service_dates; |
| 844 |
} |
| 845 |
|
| 846 |
$services = $this->get_services(); |
| 847 |
|
| 848 |
if ( ! in_array( $type, $services, true ) ) { |
| 849 |
// For backwards compatibility. |
| 850 |
$result = apply_filters_deprecated( 'orderable-service-dates', array( false, $type ), '1.8.0', 'orderable_location_service_dates' ); |
| 851 |
|
| 852 |
/** |
| 853 |
* Filter orderable service dates. |
| 854 |
* |
| 855 |
* @since 1.8.0 |
| 856 |
* @hook orderable_location_service_dates |
| 857 |
* @see Orderable_Location_Single::get_service_dates() |
| 858 |
*/ |
| 859 |
$service_dates = apply_filters( 'orderable_location_service_dates', $result, $type, $this ); |
| 860 |
|
| 861 |
wp_cache_set( $cache_key, $service_dates, '', ORDERABLE_CACHE_EXPIRATION_TIME ); |
| 862 |
|
| 863 |
return $service_dates; |
| 864 |
} |
| 865 |
|
| 866 |
$min_max_method = $this->get_delivery_calculation_method(); |
| 867 |
$lead_time_period = $this->get_lead_time_period(); |
| 868 |
$lead_days = 'days' === $lead_time_period ? $this->get_lead_time() : 0; |
| 869 |
$preorder_days = $this->get_preorder_days(); |
| 870 |
$service_days = $this->get_service_days( $type ); |
| 871 |
|
| 872 |
$start_date = new DateTime( 'now', wp_timezone() ); |
| 873 |
$start_date->setTime( 0, 0 ); // Set time to midnight 00:00:00. |
| 874 |
|
| 875 |
$date_range = new ArrayIterator( array( $start_date ) ); |
| 876 |
|
| 877 |
$counted_lead_days = 0; |
| 878 |
$counted_preorder_days = 0; |
| 879 |
|
| 880 |
if ( ! empty( $service_days ) ) { |
| 881 |
/** |
| 882 |
* Filter the max index date. |
| 883 |
* |
| 884 |
* Since the condition to break the loop that |
| 885 |
* searchs for service dates relies on the |
| 886 |
* preorder days limit, we check against $max_index_date |
| 887 |
* to prevent an infinite loop case preorder days |
| 888 |
* limit fail multiple times. |
| 889 |
* |
| 890 |
* @since 1.8.1 |
| 891 |
* @hook orderable_location_max_index_date |
| 892 |
* @param int $max_index_date The max index date value. Default: 365. |
| 893 |
* @param string $type The type. Can be 'delivery' or 'pickup'. |
| 894 |
* @param Orderable_Location_Single $location The location. |
| 895 |
* @return int New value |
| 896 |
*/ |
| 897 |
$max_index_date = apply_filters( 'orderable_location_max_index_date', 365, $type, $this ); |
| 898 |
|
| 899 |
foreach ( $date_range as $index => $date ) { |
| 900 |
// Check to avoid an infinite loop. |
| 901 |
if ( $index > $max_index_date ) { |
| 902 |
break; |
| 903 |
} |
| 904 |
|
| 905 |
// If we're at the preorder day limit, break the loop. |
| 906 |
if ( $counted_preorder_days > $preorder_days ) { |
| 907 |
break; |
| 908 |
} |
| 909 |
|
| 910 |
$timestamp = $date->getTimestamp(); |
| 911 |
$week_day = $date->format( 'w' ); |
| 912 |
$date_range->append( clone $date->modify( '+1 day' ) ); |
| 913 |
|
| 914 |
// If calculation method is 'all', we want to increase |
| 915 |
// the counters for all days. |
| 916 |
if ( 'all' === $min_max_method ) { |
| 917 |
$counted_lead_days ++; |
| 918 |
$counted_preorder_days ++; |
| 919 |
|
| 920 |
// We aren't ready to start serving up dates yet as we |
| 921 |
// haven't counted the number of lead days required. |
| 922 |
if ( $lead_days >= $counted_lead_days ) { |
| 923 |
continue; |
| 924 |
} |
| 925 |
} |
| 926 |
|
| 927 |
// If calculation method is 'weekdays' (Weekdays), we want to |
| 928 |
// increase the counters here if the day is a weekday. This |
| 929 |
// happens before checking if it's a service day. |
| 930 |
if ( 'weekdays' === $min_max_method ) { |
| 931 |
if ( Orderable_Timings::is_weekday( $timestamp ) ) { |
| 932 |
$counted_lead_days ++; |
| 933 |
$counted_preorder_days ++; |
| 934 |
} |
| 935 |
|
| 936 |
// We aren't ready to start serving up dates yet as we |
| 937 |
// haven't counted the number of lead days required. |
| 938 |
if ( $lead_days >= $counted_lead_days ) { |
| 939 |
continue; |
| 940 |
} |
| 941 |
} |
| 942 |
|
| 943 |
// If this date is a holiday, add a date to the array and continue. |
| 944 |
if ( $this->is_holiday( $timestamp, $type ) ) { |
| 945 |
continue; |
| 946 |
} |
| 947 |
|
| 948 |
// If calculation method is 'open' (Open Days), we want to |
| 949 |
// increase the counters here if the store is open. This |
| 950 |
// happens before checking if it's a service day. |
| 951 |
if ( 'open' === $min_max_method ) { |
| 952 |
if ( $this->is_open( $timestamp ) ) { |
| 953 |
$counted_lead_days ++; |
| 954 |
$counted_preorder_days ++; |
| 955 |
|
| 956 |
// We aren't ready to start serving up dates yet as we |
| 957 |
// haven't counted the number of lead days required. |
| 958 |
if ( $lead_days >= $counted_lead_days ) { |
| 959 |
continue; |
| 960 |
} |
| 961 |
} |
| 962 |
} |
| 963 |
|
| 964 |
// If this is not a service day, skip it. |
| 965 |
if ( ! in_array( (int) $week_day, array_keys( $service_days ), true ) ) { |
| 966 |
continue; |
| 967 |
} |
| 968 |
|
| 969 |
// If calculation method is 'service' (Service Days), we want to increase |
| 970 |
// the counters here, as any date past this point is a service day. |
| 971 |
if ( 'service' === $min_max_method ) { |
| 972 |
$counted_lead_days ++; |
| 973 |
$counted_preorder_days ++; |
| 974 |
|
| 975 |
// We aren't ready to start serving up dates yet as we |
| 976 |
// haven't counted the number of lead days required. |
| 977 |
if ( $lead_days >= $counted_lead_days ) { |
| 978 |
continue; |
| 979 |
} |
| 980 |
} |
| 981 |
|
| 982 |
/** |
| 983 |
* Filter whether a date is available for ordering. |
| 984 |
* |
| 985 |
* @since 1.8.0 |
| 986 |
* @hook orderable_date_available |
| 987 |
* @see Orderable_Location_Single::get_service_dates() |
| 988 |
*/ |
| 989 |
if ( ! apply_filters( 'orderable_date_available', true, $timestamp, $type, $this ) ) { |
| 990 |
continue; |
| 991 |
} |
| 992 |
|
| 993 |
$format = get_option( 'date_format' ); |
| 994 |
$slots = $this->get_slots( $timestamp, $type ); |
| 995 |
|
| 996 |
if ( empty( $slots ) ) { |
| 997 |
continue; |
| 998 |
} |
| 999 |
|
| 1000 |
$service_dates[] = array( |
| 1001 |
'timestamp' => $timestamp, |
| 1002 |
'datetime' => date( $format, $timestamp ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date |
| 1003 |
'formatted' => Orderable_Timings::get_formatted_date( $timestamp ), |
| 1004 |
'slots' => $slots, |
| 1005 |
); |
| 1006 |
} |
| 1007 |
} |
| 1008 |
|
| 1009 |
// If empty, return false; no dates available. |
| 1010 |
// Otherwise, return dates. |
| 1011 |
$service_dates = empty( $service_dates ) ? false : $service_dates; |
| 1012 |
|
| 1013 |
// For backwards compatibility. |
| 1014 |
$service_dates = apply_filters_deprecated( 'orderable-service-dates', array( $service_dates, $type ), '1.8.0', 'orderable_location_service_dates' ); |
| 1015 |
|
| 1016 |
/** |
| 1017 |
* Filter orderable service dates. |
| 1018 |
* |
| 1019 |
* @param array|bool $service_dates The service dates available. If true, |
| 1020 |
* the service doesn't require date/time selection. |
| 1021 |
* If false, a service should be selected. |
| 1022 |
* @param string $type The type of service. E.g. delivery, pickup. |
| 1023 |
* @param Orderable_Location_Single $location The location instance. |
| 1024 |
* |
| 1025 |
* @return array|bool New value |
| 1026 |
* @since 1.8.0 |
| 1027 |
* @hook orderable_location_service_dates |
| 1028 |
*/ |
| 1029 |
$service_dates = apply_filters( 'orderable_location_service_dates', $service_dates, $type, $this ); |
| 1030 |
|
| 1031 |
wp_cache_set( $cache_key, $service_dates, '', ORDERABLE_CACHE_EXPIRATION_TIME ); |
| 1032 |
|
| 1033 |
return $service_dates; |
| 1034 |
} |
| 1035 |
|
| 1036 |
/** |
| 1037 |
* Check if location has service dates for this service type. |
| 1038 |
* |
| 1039 |
* @param string $service_type Service type (delivery/pickup). |
| 1040 |
* |
| 1041 |
* @return bool |
| 1042 |
*/ |
| 1043 |
public function has_service_dates( $service_type ) { |
| 1044 |
$service_dates = $this->get_service_dates( $service_type, true ); |
| 1045 |
|
| 1046 |
return ! empty( $service_dates ); |
| 1047 |
} |
| 1048 |
|
| 1049 |
/** |
| 1050 |
* Get location address. |
| 1051 |
* |
| 1052 |
* @return array |
| 1053 |
*/ |
| 1054 |
public function get_address() { |
| 1055 |
$address = array( |
| 1056 |
'address_line_1' => $this->location_data['address_line_1'], |
| 1057 |
'address_line_2' => $this->location_data['address_line_2'], |
| 1058 |
'city' => $this->location_data['city'], |
| 1059 |
'country_state' => $this->location_data['country_state'], |
| 1060 |
'postcode_zip' => $this->location_data['postcode_zip'], |
| 1061 |
); |
| 1062 |
|
| 1063 |
return $address; |
| 1064 |
} |
| 1065 |
|
| 1066 |
/** |
| 1067 |
* Get formatted address. |
| 1068 |
* |
| 1069 |
* @return string |
| 1070 |
*/ |
| 1071 |
public function get_formatted_address() { |
| 1072 |
$address = $this->get_address(); |
| 1073 |
|
| 1074 |
$country_state = $address['country_state']; |
| 1075 |
|
| 1076 |
if ( strstr( $country_state, ':' ) ) { |
| 1077 |
$country_state = explode( ':', $country_state ); |
| 1078 |
$country = current( $country_state ); |
| 1079 |
$state = end( $country_state ); |
| 1080 |
} else { |
| 1081 |
$country = $country_state; |
| 1082 |
$state = ''; |
| 1083 |
} |
| 1084 |
|
| 1085 |
$state = empty( WC()->countries->get_states( $country )[ $state ] ) ? $state : WC()->countries->get_states( $country )[ $state ]; |
| 1086 |
$country = empty( WC()->countries->get_countries()[ $country ] ) ? $country : WC()->countries->get_countries()[ $country ]; |
| 1087 |
|
| 1088 |
$data = array( |
| 1089 |
'address_1' => $address['address_line_1'], |
| 1090 |
'address_2' => $address['address_line_2'], |
| 1091 |
'city' => $address['city'], |
| 1092 |
'state' => $state, |
| 1093 |
'country' => $country, |
| 1094 |
'postcode' => $address['postcode_zip'], |
| 1095 |
); |
| 1096 |
|
| 1097 |
return WC()->countries->get_formatted_address( $data, ', ' ); |
| 1098 |
} |
| 1099 |
|
| 1100 |
/** |
| 1101 |
* Check if location has a specific zone set. |
| 1102 |
* |
| 1103 |
* @param int $zone_id Zone ID. |
| 1104 |
* @param bool $allow_empty Allow slots with no zone set. |
| 1105 |
* |
| 1106 |
* @return bool |
| 1107 |
*/ |
| 1108 |
public function has_zone( $zone_id, $allow_empty = false ) { |
| 1109 |
global $wpdb; |
| 1110 |
|
| 1111 |
$location_id = $this->get_location_id(); |
| 1112 |
$cache_key = "has_zone_{$location_id}_{$zone_id}_{$allow_empty}"; |
| 1113 |
$cache_result = wp_cache_get( $cache_key ); |
| 1114 |
|
| 1115 |
if ( false !== $cache_result ) { |
| 1116 |
return (bool) $cache_result; |
| 1117 |
} |
| 1118 |
|
| 1119 |
$allow_empty_clause = $allow_empty ? 'OR (l.zone_id IS NULL AND ts.has_zones = 0)' : ''; |
| 1120 |
|
| 1121 |
$query = $wpdb->prepare( |
| 1122 |
"SELECT |
| 1123 |
COUNT(*) |
| 1124 |
FROM |
| 1125 |
{$wpdb->prefix}orderable_location_delivery_zones_lookup l |
| 1126 |
LEFT JOIN |
| 1127 |
{$wpdb->prefix}orderable_location_time_slots ts |
| 1128 |
ON l.time_slot_id = ts.time_slot_id |
| 1129 |
WHERE |
| 1130 |
l.location_id = %d |
| 1131 |
AND |
| 1132 |
( l.zone_id = %d {$allow_empty_clause} )", |
| 1133 |
$location_id, |
| 1134 |
$zone_id |
| 1135 |
); |
| 1136 |
|
| 1137 |
$result = $wpdb->get_var( $query ); |
| 1138 |
|
| 1139 |
$has_zone = $result > 0; |
| 1140 |
|
| 1141 |
wp_cache_set( $cache_key, (int) $has_zone ); |
| 1142 |
|
| 1143 |
return $has_zone; |
| 1144 |
} |
| 1145 |
|
| 1146 |
/** |
| 1147 |
* Update location title. |
| 1148 |
* |
| 1149 |
* @param string $title New title. |
| 1150 |
*/ |
| 1151 |
public function update_title( $title ) { |
| 1152 |
global $wpdb; |
| 1153 |
|
| 1154 |
$this->location_data['title'] = $title; |
| 1155 |
|
| 1156 |
wp_update_post( |
| 1157 |
array( |
| 1158 |
'ID' => $this->location_data['post_id'], |
| 1159 |
'post_title' => $title, |
| 1160 |
) |
| 1161 |
); |
| 1162 |
|
| 1163 |
$wpdb->update( |
| 1164 |
$wpdb->prefix . 'orderable_locations', |
| 1165 |
array( |
| 1166 |
'title' => $title, |
| 1167 |
), |
| 1168 |
array( |
| 1169 |
'location_id' => $this->location_data['location_id'], |
| 1170 |
) |
| 1171 |
); |
| 1172 |
|
| 1173 |
} |
| 1174 |
} |
| 1175 |
|