PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.3
Yatra – Travel Booking & Tour Operator Software v3.0.3
3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 All 83 releases
yatra / app / Services / BlockDataService.php

BlockDataService.php in Yatra – Travel Booking & Tour Operator Software 3.0.3, at app/Services/BlockDataService.php

488 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Services;
6
7 use Yatra\Shortcodes\ActivityShortcode;
8 use Yatra\Shortcodes\DestinationShortcode;
9 use Yatra\Shortcodes\TripCategoryShortcode;
10
11 /**
12 * Block Data Service
13 *
14 * Shared service for rendering blocks and shortcodes
15 * Provides reusable data fetching and template rendering logic
16 *
17 * @package Yatra\Services
18 * @since 3.0.0
19 */
20 class BlockDataService
21 {
22 /** Max grid columns for tour / activity / destination listing blocks (matches block editor RangeControl). */
23 private const LISTING_COLUMNS_MAX = 6;
24
25 /**
26 * Defaults aligned with each block's block.json (REST and ServerSideRender rely on full merge).
27 *
28 * @return array<string, mixed>
29 */
30 private static function defaultTourBlockAttributes(): array
31 {
32 return [
33 'order' => 'desc',
34 'featured' => false,
35 'per_page' => 10,
36 'columns' => 3,
37 'title' => 'Our Trips',
38 'show_pagination' => true,
39 ];
40 }
41
42 /**
43 * @return array<string, mixed>
44 */
45 private static function defaultActivityBlockAttributes(): array
46 {
47 return [
48 'order' => 'asc',
49 'columns' => 3,
50 'per_page' => 10,
51 'title' => 'Activity Listings',
52 'show_pagination' => true,
53 ];
54 }
55
56 /**
57 * @return array<string, mixed>
58 */
59 private static function defaultDestinationBlockAttributes(): array
60 {
61 return [
62 'order' => 'asc',
63 'columns' => 3,
64 'per_page' => 10,
65 'title' => 'Destination Showcase',
66 'show_pagination' => true,
67 ];
68 }
69
70 /**
71 * @return array<string, mixed>
72 */
73 private static function defaultTripCategoryBlockAttributes(): array
74 {
75 return [
76 'order' => 'desc',
77 'columns' => 3,
78 'per_page' => 10,
79 'title' => 'Trip Categories',
80 'show_pagination' => true,
81 'category' => '',
82 'show_trip_count' => true,
83 'show_description' => true,
84 'show_image' => true,
85 'hide_empty' => true,
86 'featured_only' => false,
87 ];
88 }
89
90 /**
91 * Coerce REST/block booleans and legacy string values.
92 *
93 * @param mixed $value
94 */
95 private static function coerceToBool($value, bool $default): bool
96 {
97 if ($value === null || $value === '') {
98 return $default;
99 }
100 if (is_bool($value)) {
101 return $value;
102 }
103 if (is_int($value) || is_float($value)) {
104 return (bool) $value;
105 }
106 $s = strtolower((string) $value);
107
108 return in_array($s, ['1', 'true', 'yes', 'on'], true);
109 }
110
111 /**
112 * Normalize trip/tour attributes in place (shortcode extras like category are preserved).
113 *
114 * @param array<string, mixed> $atts
115 */
116 private static function normalizeTripShortcodeAttributes(array &$atts): void
117 {
118 $order = strtolower((string) ($atts['order'] ?? 'desc'));
119 $atts['order'] = in_array($order, ['asc', 'desc'], true) ? $order : 'desc';
120
121 $atts['featured'] = self::coerceToBool($atts['featured'] ?? false, false) ? '1' : '0';
122
123 $perPage = (int) ($atts['per_page'] ?? 10);
124 if ($perPage === -1) {
125 $perPage = 10;
126 }
127 $atts['per_page'] = (string) max(1, min(100, $perPage));
128
129 $cols = (int) ($atts['columns'] ?? 3);
130 $atts['columns'] = (string) max(1, min(self::LISTING_COLUMNS_MAX, $cols));
131
132 $atts['title'] = sanitize_text_field((string) ($atts['title'] ?? 'Our Trips'));
133
134 $atts['show_pagination'] = self::coerceToBool($atts['show_pagination'] ?? true, true) ? 'yes' : 'no';
135 }
136
137 /**
138 * Render trip/tour - Shared method for both blocks and shortcodes
139 *
140 * @param array $attributes Block or shortcode attributes
141 * @return string Rendered HTML
142 */
143 public static function renderTrip(array $attributes): string
144 {
145 try {
146 $atts = wp_parse_args(is_array($attributes) ? $attributes : [], self::defaultTourBlockAttributes());
147 self::normalizeTripShortcodeAttributes($atts);
148
149 // Get trips using Yatra's service
150 $trips_data = self::getTrips($atts);
151
152 $per_page = max(1, (int) ($atts['per_page'] ?? 10));
153 $atts['per_page'] = $per_page;
154
155 // Prepare data for template
156 $data = [
157 'trips' => [
158 'trips' => $trips_data['trips'] ?? [],
159 'max_pages' => $trips_data['max_pages'] ?? 1,
160 'current_page' => $trips_data['current_page'] ?? 1,
161 'total_found' => $trips_data['total_found'] ?? 0,
162 'per_page' => $per_page
163 ],
164 'atts' => $atts,
165 'current_page' => $trips_data['current_page'] ?? 1,
166 'max_pages' => $trips_data['max_pages'] ?? 1,
167 'total_found' => $trips_data['total_found'] ?? 0,
168 'per_page' => $per_page
169 ];
170
171 // Enqueue assets
172 self::enqueueTripAssets();
173
174 // Load template
175 $result = self::loadTemplate('shortcodes/trip.php', $data);
176
177
178 return $result;
179 } catch (\Exception $e) {
180
181 return '<div class="yatra-error">Trip rendering failed</div>';
182 } catch (\Error $e) {
183
184 return '<div class="yatra-error">Trip rendering failed</div>';
185 }
186 }
187
188 /**
189 * Render trip/tour block - Backward compatibility method
190 *
191 * @param array $attributes Block attributes
192 * @return string Rendered HTML
193 */
194 public static function renderTripBlock(array $attributes): string
195 {
196 return self::renderTrip($attributes);
197 }
198
199 /**
200 * Enqueue trip assets
201 */
202 private static function enqueueTripAssets(): void
203 {
204 \Yatra\Providers\FrontendAssetsProvider::registerCoreFrontendStylesheets();
205 $cssPath = \YATRA_PLUGIN_PATH . 'assets/css/shortcodes/trip-shortcode.css';
206 $cssVer = is_readable($cssPath) ? \YATRA_VERSION . '.' . filemtime($cssPath) : \YATRA_VERSION;
207 wp_enqueue_style(
208 'yatra-trip-shortcode',
209 \YATRA_PLUGIN_URL . 'assets/css/shortcodes/trip-shortcode.css',
210 \Yatra\Providers\FrontendAssetsProvider::shortcodeStyleDependencies(),
211 $cssVer
212 );
213 wp_enqueue_script('yatra-trip-shortcode', \YATRA_PLUGIN_URL . 'assets/js/trip-shortcode.js', array('jquery'), \YATRA_VERSION, true);
214
215 // Localize script for AJAX
216 wp_localize_script('yatra-trip-shortcode', 'yatraTripShortcode', [
217 'ajaxurl' => admin_url('admin-ajax.php'),
218 'nonce' => wp_create_nonce('yatra_trip_shortcode_nonce')
219 ]);
220 }
221
222 /**
223 * Load template file
224 */
225 private static function loadTemplate(string $template_path, array $data = []): string
226 {
227 $full_path = \YATRA_PLUGIN_PATH . 'templates/' . $template_path;
228
229 if (!file_exists($full_path)) {
230 if (defined('WP_DEBUG') && WP_DEBUG) {
231 return sprintf(
232 '<div class="yatra-error">Template not found: %s</div>',
233 esc_html($full_path)
234 );
235 }
236 return '';
237 }
238
239 // Extract data to make variables available in template
240 if (!empty($data)) {
241 extract($data);
242 }
243
244 ob_start();
245 include $full_path;
246 return ob_get_clean();
247 }
248
249 /**
250 * Get trips data - copied from TripShortcode
251 */
252 private static function getTrips(array $atts): array
253 {
254 try {
255 $tripService = new \Yatra\Services\TripService();
256
257 // Get current page from query string or attributes (for AJAX)
258 $current_page = isset($atts['current_page']) ? (int) $atts['current_page'] : (isset($_GET['trip_page']) ? (int) $_GET['trip_page'] : 1);
259 $per_page = max(1, (int) ($atts['per_page'] ?? 10));
260 $offset = ($current_page - 1) * $per_page;
261
262 $order = strtolower((string) ($atts['order'] ?? 'desc'));
263
264 // Start with very basic arguments to ensure we get trips
265 $args = [
266 'limit' => $per_page,
267 'offset' => $offset,
268 'order_by' => 'created_at',
269 'order' => $order === 'asc' ? 'ASC' : 'DESC',
270 ];
271
272 // Add featured filter if requested
273 $featured = (string) ($atts['featured'] ?? '0');
274 if ($featured === '1') {
275 $args['where']['is_featured'] = 1;
276 }
277
278 // Get total count for pagination
279 $count_args = $args;
280 unset($count_args['limit']);
281 unset($count_args['offset']);
282 $total_trips = $tripService->count($count_args);
283
284 // Get trips using the service
285 $trips_data = $tripService->getActiveTrips($args);
286
287
288
289 $trips = [];
290 foreach ($trips_data as $tripData) {
291 // Convert to Trip model
292 $trip = \Yatra\Models\Trip::fromStdClass($tripData);
293
294 // Add basic data needed for the card
295 // Note: reviews are loaded elsewhere; bookings_count is attached below
296 $trip->reviews = [];
297
298 $trips[] = $trip;
299 }
300
301 // Attach bookings_count (computed from bookings table) for just these trips
302 $tripIds = array_map(static function ($t) {
303 return isset($t->id) ? (int) $t->id : 0;
304 }, $trips);
305 $tripIds = array_values(array_filter($tripIds));
306 if (!empty($tripIds)) {
307 $bookingsCountMap = $tripService->getBookingsCountMap($tripIds);
308 foreach ($trips as $t) {
309 $tId = isset($t->id) ? (int) $t->id : 0;
310 if ($tId > 0) {
311 $t->bookings_count = (int) ($bookingsCountMap[$tId] ?? 0);
312 }
313 }
314 }
315
316 // Calculate pagination data
317 $max_pages = $per_page > 0 ? ceil($total_trips / $per_page) : 1;
318
319 return [
320 'trips' => $trips,
321 'max_pages' => $max_pages,
322 'current_page' => $current_page,
323 'total_found' => $total_trips,
324 'debug_info' => [
325 'args_used' => $args,
326 'raw_count' => count($trips_data)
327 ]
328 ];
329
330 } catch (\Exception $e) {
331
332
333 return [
334 'trips' => [],
335 'max_pages' => 1,
336 'current_page' => 1,
337 'total_found' => 0
338 ];
339 }
340 }
341
342 /**
343 * Render activity block or shortcode
344 *
345 * @param array $attributes Block or shortcode attributes
346 * @return string Rendered HTML
347 */
348 public static function renderActivityBlock(array $attributes): string
349 {
350 // Create shortcode instance to reuse its logic
351 $shortcode = new ActivityShortcode();
352
353 $merged = wp_parse_args(is_array($attributes) ? $attributes : [], self::defaultActivityBlockAttributes());
354
355 // Use shortcode's render method
356 return $shortcode->render(self::mapActivityAttributes($merged));
357 }
358
359 /**
360 * Render destination block or shortcode
361 *
362 * @param array $attributes Block or shortcode attributes
363 * @return string Rendered HTML
364 */
365 public static function renderDestinationBlock(array $attributes): string
366 {
367 // Create shortcode instance to reuse its logic
368 $shortcode = new DestinationShortcode();
369
370 $merged = wp_parse_args(is_array($attributes) ? $attributes : [], self::defaultDestinationBlockAttributes());
371
372 // Use shortcode's render method
373 return $shortcode->render(self::mapDestinationAttributes($merged));
374 }
375
376 /**
377 * Render trip category block (same card UI as destinations).
378 *
379 * @param array<string, mixed> $attributes
380 */
381 public static function renderTripCategoryBlock(array $attributes): string
382 {
383 $shortcode = new TripCategoryShortcode();
384 $merged = wp_parse_args(is_array($attributes) ? $attributes : [], self::defaultTripCategoryBlockAttributes());
385
386 return $shortcode->render(self::mapTripCategoryAttributes($merged));
387 }
388
389 /**
390 * Map activity block attributes to shortcode format (full set for shortcode_atts merge).
391 *
392 * @param array<string, mixed> $attributes Merged with defaults
393 * @return array<string, string>
394 */
395 private static function mapActivityAttributes(array $attributes): array
396 {
397 $order = strtolower((string) ($attributes['order'] ?? 'asc'));
398 $order = in_array($order, ['asc', 'desc'], true) ? $order : 'asc';
399
400 $perPage = (int) ($attributes['per_page'] ?? 10);
401 if ($perPage === -1) {
402 $perPage = 10;
403 }
404 $perPage = max(1, min(100, $perPage));
405
406 $cols = max(1, min(self::LISTING_COLUMNS_MAX, (int) ($attributes['columns'] ?? 3)));
407
408 $showPag = self::coerceToBool($attributes['show_pagination'] ?? true, true);
409
410 return [
411 'order' => $order,
412 'per_page' => (string) $perPage,
413 'columns' => (string) $cols,
414 'title' => sanitize_text_field((string) ($attributes['title'] ?? 'Activity Listings')),
415 'show_pagination' => $showPag ? 'yes' : 'no',
416 ];
417 }
418
419 /**
420 * @param array<string, mixed> $attributes Merged with defaults
421 * @return array<string, string>
422 */
423 private static function mapDestinationAttributes(array $attributes): array
424 {
425 $order = strtolower((string) ($attributes['order'] ?? 'asc'));
426 $order = in_array($order, ['asc', 'desc'], true) ? $order : 'asc';
427
428 $perPage = (int) ($attributes['per_page'] ?? 10);
429 if ($perPage === -1) {
430 $perPage = 10;
431 }
432 $perPage = max(1, min(100, $perPage));
433
434 $cols = max(1, min(self::LISTING_COLUMNS_MAX, (int) ($attributes['columns'] ?? 3)));
435
436 $showPag = self::coerceToBool($attributes['show_pagination'] ?? true, true);
437
438 return [
439 'order' => $order,
440 'per_page' => (string) $perPage,
441 'columns' => (string) $cols,
442 'title' => sanitize_text_field((string) ($attributes['title'] ?? 'Destination Showcase')),
443 'show_pagination' => $showPag ? 'yes' : 'no',
444 ];
445 }
446
447 /**
448 * @param array<string, mixed> $attributes
449 * @return array<string, string>
450 */
451 private static function mapTripCategoryAttributes(array $attributes): array
452 {
453 $order = strtolower((string) ($attributes['order'] ?? 'desc'));
454 $order = in_array($order, ['asc', 'desc'], true) ? $order : 'desc';
455
456 $perPage = (int) ($attributes['per_page'] ?? 10);
457 if ($perPage === -1) {
458 $perPage = 10;
459 }
460 $perPage = max(1, min(100, $perPage));
461
462 $cols = max(1, min(self::LISTING_COLUMNS_MAX, (int) ($attributes['columns'] ?? 3)));
463
464 $showPag = self::coerceToBool($attributes['show_pagination'] ?? true, true);
465 $showTripCount = self::coerceToBool($attributes['show_trip_count'] ?? true, true);
466 $showDescription = self::coerceToBool($attributes['show_description'] ?? true, true);
467 $showImage = self::coerceToBool($attributes['show_image'] ?? true, true);
468 $hideEmpty = self::coerceToBool($attributes['hide_empty'] ?? true, true);
469 $featuredOnly = self::coerceToBool($attributes['featured_only'] ?? false, false);
470
471 $category = isset($attributes['category']) ? sanitize_text_field((string) $attributes['category']) : '';
472
473 return [
474 'order' => $order,
475 'per_page' => (string) $perPage,
476 'columns' => (string) $cols,
477 'title' => sanitize_text_field((string) ($attributes['title'] ?? 'Trip Categories')),
478 'show_pagination' => $showPag ? 'yes' : 'no',
479 'category' => $category,
480 'show_trip_count' => $showTripCount ? 'yes' : 'no',
481 'show_description' => $showDescription ? 'yes' : 'no',
482 'show_image' => $showImage ? 'yes' : 'no',
483 'hide_empty' => $hideEmpty ? 'yes' : 'no',
484 'featured_only' => $featuredOnly ? 'yes' : 'no',
485 ];
486 }
487 }
488