PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.7
Yatra – Travel Booking & Tour Operator Software v3.0.2.7
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.2.7, at app/Services/BlockDataService.php

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