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 / Shortcodes / TripCategoryShortcode.php

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

397 lines 14.0 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\Shortcodes;
6
7 use Yatra\Services\SettingsService;
8
9 /**
10 * Trip category listing shortcode — same card UI as {@see DestinationShortcode}.
11 */
12 class TripCategoryShortcode extends BaseShortcode
13 {
14 public function __construct()
15 {
16 parent::__construct('yatra_trip_category', [
17 'order' => 'desc',
18 'per_page' => '10',
19 'columns' => '3',
20 'show_trip_count' => 'yes',
21 'show_description' => 'yes',
22 'show_image' => 'yes',
23 'show_pagination' => 'yes',
24 'category' => '',
25 'hide_empty' => 'yes',
26 'featured_only' => 'no',
27 'title' => 'Trip Categories',
28 ]);
29 }
30
31 protected function renderContent(array $atts): string
32 {
33 $atts = shortcode_atts($this->default_attributes, $atts, $this->tag);
34
35 $per_page = 10;
36 if (!empty($atts['per_page']) && is_numeric($atts['per_page'])) {
37 $per_page = (int) $atts['per_page'];
38 }
39 $atts['per_page'] = $per_page;
40
41 $categories_data = $this->getCategories($atts);
42
43 $data = [
44 'categories' => $categories_data['categories'] ?? [],
45 'atts' => $atts,
46 'current_page' => $categories_data['current_page'] ?? 1,
47 'max_pages' => $categories_data['max_pages'] ?? 1,
48 'total_found' => $categories_data['total_found'] ?? 0,
49 'per_page' => $per_page,
50 ];
51
52 $destinationCssPath = YATRA_PLUGIN_PATH . 'assets/css/shortcodes/destination-shortcode.css';
53 $destinationCssVer = is_readable($destinationCssPath) ? YATRA_VERSION . '.' . filemtime($destinationCssPath) : YATRA_VERSION;
54 wp_enqueue_style(
55 'yatra-destination-shortcode',
56 YATRA_PLUGIN_URL . 'assets/css/shortcodes/destination-shortcode.css',
57 \Yatra\Providers\FrontendAssetsProvider::shortcodeStyleDependencies(),
58 $destinationCssVer
59 );
60
61 wp_enqueue_script(
62 'yatra-trip-category-shortcode',
63 YATRA_PLUGIN_URL . 'assets/js/trip-category-shortcode.js',
64 ['jquery'],
65 YATRA_VERSION,
66 true
67 );
68
69 wp_localize_script('yatra-trip-category-shortcode', 'yatraTripCategoryShortcode', [
70 'ajaxurl' => admin_url('admin-ajax.php'),
71 'nonce' => wp_create_nonce('yatra_trip_category_shortcode_nonce'),
72 ]);
73
74 return $this->loadTemplate('shortcodes/trip-category.php', $data);
75 }
76
77 /**
78 * @return array{categories: array, current_page: int, max_pages: int, total_found: int, per_page: int}
79 */
80 public function getCategories(array $atts): array
81 {
82 try {
83 $categoryService = new \Yatra\Services\TripCategoryService();
84
85 $current_page = isset($atts['current_page'])
86 ? (int) $atts['current_page']
87 : (isset($_GET['trip_category_page']) ? (int) $_GET['trip_category_page'] : 1);
88
89 $per_page = !empty($atts['per_page']) ? (int) $atts['per_page'] : 10;
90 $per_page = max(1, min($per_page, 100));
91 $offset = ($current_page - 1) * $per_page;
92
93 $args = [
94 'limit' => $per_page,
95 'offset' => $offset,
96 'order_by' => 'name',
97 'order' => ($atts['order'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC',
98 'where' => [
99 'status' => 'publish',
100 ],
101 ];
102
103 if (!empty($atts['category'])) {
104 $args['where']['slug'] = array_map('trim', explode(',', (string) $atts['category']));
105 }
106
107 $count_args = $args;
108 unset($count_args['limit'], $count_args['offset']);
109 $total_categories = $categoryService->count($count_args);
110
111 $result = $categoryService->getAll($args);
112
113 $categories = [];
114
115 foreach ($result as $categoryData) {
116 global $wpdb;
117
118 $tripClassificationsTable = \Yatra\Database\Tables\TripClassificationsTable::getTableName();
119 $tripsTable = \Yatra\Database\Tables\TripsTable::getTableName();
120
121 $trip_ids = $wpdb->get_col($wpdb->prepare(
122 "SELECT tc.trip_id
123 FROM {$tripClassificationsTable} tc
124 INNER JOIN {$tripsTable} t ON tc.trip_id = t.id
125 WHERE tc.classification_id = %d
126 AND tc.classification_type = 'category'
127 AND t.status = 'publish'",
128 $categoryData->id
129 ));
130
131 $trip_count = count($trip_ids);
132
133 $trips = [];
134 if (!empty($trip_ids)) {
135 $placeholders = implode(',', array_fill(0, count($trip_ids), '%d'));
136 $trips = $wpdb->get_results($wpdb->prepare(
137 "SELECT * FROM {$tripsTable}
138 WHERE id IN ({$placeholders})
139 AND status = 'publish'
140 ORDER BY created_at DESC
141 LIMIT 6",
142 ...$trip_ids
143 ));
144 }
145
146 $min_price = null;
147 $max_price = null;
148 $durations = [];
149 $group_sizes = [];
150 $best_seasons = [];
151
152 $total_rating_sum = 0;
153 $total_review_count = 0;
154
155 if (!empty($trip_ids)) {
156 $reviewsTable = \Yatra\Database\Tables\ReviewsTable::getTableName();
157 $placeholders = implode(',', array_fill(0, count($trip_ids), '%d'));
158
159 $reviews = $wpdb->get_results($wpdb->prepare(
160 "SELECT rating, COUNT(*) as review_count
161 FROM {$reviewsTable}
162 WHERE trip_id IN ({$placeholders})
163 AND status = 'approved'",
164 ...$trip_ids
165 ));
166
167 foreach ($reviews as $review) {
168 $total_rating_sum += $review->rating * $review->review_count;
169 $total_review_count += $review->review_count;
170 }
171 }
172
173 $avg_rating = $total_review_count > 0 ? $total_rating_sum / $total_review_count : 0;
174
175 foreach ($trips as $trip) {
176 $effective = \Yatra\Services\TripPricingService::getEffectivePrice($trip);
177 if ($effective > 0) {
178 if ($min_price === null || $effective < $min_price) {
179 $min_price = $effective;
180 }
181 if ($max_price === null || $effective > $max_price) {
182 $max_price = $effective;
183 }
184 }
185 if (!empty($trip->duration)) {
186 $durations[] = $trip->duration;
187 }
188 if (!empty($trip->max_group_size)) {
189 $group_sizes[] = $trip->max_group_size;
190 }
191 if (!empty($trip->best_season)) {
192 $best_seasons[] = $trip->best_season;
193 }
194 }
195
196 $final_avg_rating = $avg_rating;
197 $avg_duration = !empty($durations) ? array_sum($durations) / count($durations) : 0;
198 $avg_group_size = !empty($group_sizes) ? round(array_sum($group_sizes) / count($group_sizes)) : 0;
199 $best_season = !empty($best_seasons) ? $this->getMostCommonSeason($best_seasons) : 'Summer';
200
201 $categories[] = [
202 'term' => $categoryData,
203 'trips' => $trips,
204 'trip_count' => $trip_count,
205 'description' => $categoryData->description ?? '',
206 'image' => $this->getCategoryImage($categoryData, $trips),
207 'link' => $this->getCategoryLink($categoryData),
208 'min_price' => $min_price,
209 'max_price' => $max_price,
210 'avg_rating' => $final_avg_rating,
211 'rating_count' => $total_review_count,
212 'avg_duration' => $avg_duration,
213 'avg_group_size' => $avg_group_size,
214 'best_season' => $best_season,
215 ];
216 }
217
218 if (($atts['hide_empty'] ?? 'yes') === 'yes') {
219 $categories = array_filter($categories, static function ($row) {
220 return !empty($row['term']->name) && !empty($row['term']->slug);
221 });
222 }
223
224 if (($atts['featured_only'] ?? 'no') === 'yes') {
225 $categories = array_filter($categories, static function ($row) {
226 $t = $row['term'];
227
228 return (isset($t->is_featured) && (int) $t->is_featured === 1)
229 || (isset($t->featured) && (int) $t->featured === 1);
230 });
231 }
232
233 $max_pages = $per_page > 0 ? (int) ceil($total_categories / $per_page) : 1;
234
235 return [
236 'categories' => $categories,
237 'current_page' => $current_page,
238 'max_pages' => max(1, $max_pages),
239 'total_found' => $total_categories,
240 'per_page' => $per_page,
241 ];
242 } catch (\Exception $e) {
243 if (defined('WP_DEBUG') && WP_DEBUG) {
244 }
245
246 return [
247 'categories' => [],
248 'current_page' => 1,
249 'max_pages' => 1,
250 'total_found' => 0,
251 'per_page' => (int) ($atts['per_page'] ?? 10),
252 ];
253 }
254 }
255
256 private function getMostCommonSeason(array $seasons): string
257 {
258 if (empty($seasons)) {
259 return 'Summer';
260 }
261 $counts = array_count_values($seasons);
262 arsort($counts);
263
264 return array_key_first($counts);
265 }
266
267 private function getImageUrlFromCategoryIcon($icon): string
268 {
269 if ($icon === null || $icon === '') {
270 return '';
271 }
272
273 if (is_string($icon)) {
274 $decoded = json_decode($icon, true);
275 if (is_array($decoded)) {
276 $icon = $decoded;
277 } else {
278 $icon = maybe_unserialize($icon);
279 }
280 }
281
282 if (!is_array($icon)) {
283 return '';
284 }
285
286 $type = $icon['type'] ?? $icon[0] ?? '';
287 $value = $icon['value'] ?? $icon[1] ?? '';
288
289 if ($type !== 'image' || $value === '' || $value === null) {
290 return '';
291 }
292
293 if (is_numeric($value)) {
294 $url = wp_get_attachment_image_url((int) $value, 'large');
295
296 return $url ?: '';
297 }
298
299 if (is_string($value) && filter_var($value, FILTER_VALIDATE_URL)) {
300 return $value;
301 }
302
303 return '';
304 }
305
306 /**
307 * @return array<string, mixed>
308 */
309 private function decodeCategoryMetadata($raw): array
310 {
311 if ($raw === null || $raw === '') {
312 return [];
313 }
314 if (is_array($raw)) {
315 return $raw;
316 }
317 if (!is_string($raw)) {
318 return [];
319 }
320 $decoded = json_decode($raw, true);
321 if (is_array($decoded)) {
322 return $decoded;
323 }
324 $unserialized = maybe_unserialize($raw);
325
326 return is_array($unserialized) ? $unserialized : [];
327 }
328
329 /**
330 * @param object $category Row from classifications (type category)
331 * @param array<int, object> $trips
332 */
333 private function getCategoryImage($category, array $trips = []): string
334 {
335 if (isset($category->image) && !empty($category->image)) {
336 return $category->image;
337 }
338 if (isset($category->banner) && !empty($category->banner)) {
339 return $category->banner;
340 }
341 if (isset($category->thumbnail) && !empty($category->thumbnail)) {
342 return is_numeric($category->thumbnail)
343 ? (string) wp_get_attachment_url((int) $category->thumbnail)
344 : $category->thumbnail;
345 }
346 if (isset($category->cover_image) && !empty($category->cover_image)) {
347 return $category->cover_image;
348 }
349 if (isset($category->hero_image) && !empty($category->hero_image)) {
350 return $category->hero_image;
351 }
352
353 $fromIcon = $this->getImageUrlFromCategoryIcon($category->icon ?? null);
354 if ($fromIcon !== '') {
355 return $fromIcon;
356 }
357
358 if (isset($category->metadata) && $category->metadata !== '') {
359 $metadata = $this->decodeCategoryMetadata($category->metadata);
360 if ($metadata !== []) {
361 $image_fields = ['image', 'thumbnail', 'banner', 'featured_image', 'cover_image', 'hero_image', 'image_id'];
362 foreach ($image_fields as $field) {
363 if (!empty($metadata[$field])) {
364 $val = $metadata[$field];
365
366 return is_numeric($val) ? (string) wp_get_attachment_url((int) $val) : (string) $val;
367 }
368 }
369 }
370 }
371
372 foreach ($trips as $trip) {
373 if (!empty($trip->featured_image) && is_numeric($trip->featured_image)) {
374 $url = wp_get_attachment_image_url((int) $trip->featured_image, 'large');
375 if ($url !== false && $url !== '') {
376 return $url;
377 }
378 }
379 }
380
381 return YATRA_PLUGIN_URL . 'assets/images/placeholder.png';
382 }
383
384 private function getCategoryLink($category): string
385 {
386 if (isset($category->slug) && function_exists('yatra_get_category_permalink')) {
387 $url = yatra_get_category_permalink($category);
388
389 return $url !== '' ? $url : '#';
390 }
391
392 $base = SettingsService::getString('trip_category_base', 'trip-category');
393
394 return isset($category->slug) ? home_url('/' . $base . '/' . $category->slug . '/') : '#';
395 }
396 }
397