PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.8
Yatra – Travel Booking & Tour Operator Software v3.0.2.8
3.0.16 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 All 84 releases
yatra / app / Services / CategoryService.php

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

405 lines 13.5 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\Repositories\CategoryRepository;
8 use Yatra\Repositories\TripCategoryRepository;
9 use Yatra\Helpers\SlugHelper;
10 use Yatra\Helpers\FormatHelper;
11 use Yatra\Database\Tables\ClassificationsTable;
12
13 /**
14 * Category Service
15 * Contains business logic for categories
16 */
17 class CategoryService extends BaseService
18 {
19 /**
20 * @var CategoryRepository
21 */
22 private CategoryRepository $repository;
23
24 /**
25 * Constructor
26 */
27 public function __construct()
28 {
29 $this->repository = new CategoryRepository();
30 }
31
32 /**
33 * Get repository
34 */
35 protected function getRepository(): CategoryRepository
36 {
37 return $this->repository;
38 }
39
40 /**
41 * Process before create
42 */
43 protected function processBeforeCreate(array $data): array
44 {
45 // Set the type to 'category' for the ClassificationsTable
46 $data['type'] = 'category';
47
48 // Sanitize name
49 if (isset($data['name'])) {
50 $data['name'] = sanitize_text_field($data['name']);
51 }
52
53 // Always auto-generate slug from name (backend ensures uniqueness)
54 if (!empty($data['name'])) {
55 $data['slug'] = SlugHelper::generateUniqueFromDatabase(
56 $data['name'],
57 ClassificationsTable::getTableName(),
58 'slug'
59 );
60 } elseif (isset($data['slug'])) {
61 // If name is empty but slug is provided, sanitize it
62 $data['slug'] = SlugHelper::generate($data['slug']);
63 }
64
65 // Sanitize Quill HTML description
66 if (isset($data['description'])) {
67 $data['description'] = FormatHelper::sanitizeQuillHtml($data['description']);
68 }
69
70 // Sanitize status
71 if (isset($data['status'])) {
72 // Validate status
73 $allowed_statuses = ['draft', 'publish', 'trash'];
74 $data['status'] = in_array($data['status'], $allowed_statuses, true)
75 ? $data['status']
76 : 'draft';
77 } else {
78 $data['status'] = 'draft';
79 }
80
81 // Set created_by and updated_by to current user
82 $current_user_id = get_current_user_id();
83 $data['created_by'] = absint($current_user_id);
84 $data['updated_by'] = absint($current_user_id);
85
86 // Sanitize and serialize icon if it's an array
87 if (isset($data['icon'])) {
88 if (is_array($data['icon'])) {
89 // Convert URL back to attachment ID if possible
90 if ($data['icon']['type'] === 'image' && !empty($data['icon']['value'])) {
91 $value = $data['icon']['value'];
92
93 // If it's a URL, try to find the attachment ID
94 if (filter_var($value, FILTER_VALIDATE_URL)) {
95 $attachment_id = attachment_url_to_postid($value);
96 if ($attachment_id) {
97 $data['icon']['value'] = $attachment_id;
98 }
99 }
100 // If it's already numeric, keep it as is
101 elseif (is_numeric($value)) {
102 $data['icon']['value'] = (int) $value;
103 }
104 }
105
106 $data['icon'] = maybe_serialize($data['icon']);
107 }
108 }
109
110 // Handle SEO metadata
111 if (isset($data['seo_title']) || isset($data['seo_description']) || isset($data['seo_keywords'])) {
112 $existing_metadata = [];
113 if (isset($data['metadata']) && is_array($data['metadata'])) {
114 $existing_metadata = $data['metadata'];
115 } elseif (isset($data['metadata'])) {
116 $existing_metadata = maybe_unserialize($data['metadata']);
117 }
118
119 // Add SEO fields to metadata
120 if (isset($data['seo_title'])) {
121 $existing_metadata['seo_title'] = sanitize_text_field($data['seo_title']);
122 unset($data['seo_title']); // Remove from main data
123 }
124 if (isset($data['seo_description'])) {
125 $existing_metadata['seo_description'] = sanitize_textarea_field($data['seo_description']);
126 unset($data['seo_description']); // Remove from main data
127 }
128 if (isset($data['seo_keywords'])) {
129 $existing_metadata['seo_keywords'] = sanitize_text_field($data['seo_keywords']);
130 unset($data['seo_keywords']); // Remove from main data
131 }
132
133 $data['metadata'] = $existing_metadata;
134 }
135
136 // Sanitize metadata if it's an array
137 if (isset($data['metadata'])) {
138 if (is_array($data['metadata'])) {
139 $data['metadata'] = maybe_serialize($data['metadata']);
140 }
141 }
142
143 return $data;
144 }
145
146 /**
147 * Process before update
148 */
149 protected function processBeforeUpdate(int $id, array $data): array
150 {
151
152 // Ensure the type remains 'category' for the ClassificationsTable
153 $data['type'] = 'category';
154
155 // Sanitize name
156 if (isset($data['name'])) {
157 $data['name'] = sanitize_text_field($data['name']);
158 }
159
160 // Handle slug in EDIT mode: Only update if explicitly provided
161 // Do NOT auto-generate from name in edit mode
162 if (isset($data['slug']) && !empty($data['slug'])) {
163 // Slug was explicitly provided - ensure uniqueness
164 $data['slug'] = SlugHelper::generateUniqueFromDatabase(
165 $data['slug'],
166 ClassificationsTable::getTableName(),
167 'slug',
168 $id // Exclude current record when checking uniqueness
169 );
170 }
171 // If slug is not provided, don't modify it (keep existing slug)
172
173 // Sanitize Quill HTML description
174 if (isset($data['description'])) {
175 $data['description'] = FormatHelper::sanitizeQuillHtml($data['description']);
176 }
177
178 // Sanitize status
179 if (isset($data['status'])) {
180 // Validate status
181 $allowed_statuses = ['draft', 'publish', 'trash'];
182 $data['status'] = in_array($data['status'], $allowed_statuses, true)
183 ? $data['status']
184 : 'draft';
185 }
186
187 // Set updated_by to current user
188 $data['updated_by'] = absint(get_current_user_id());
189
190 // Sanitize and serialize icon if it's an array
191 if (isset($data['icon'])) {
192 if (is_array($data['icon'])) {
193 // Convert URL back to attachment ID if possible
194 if ($data['icon']['type'] === 'image' && !empty($data['icon']['value'])) {
195 $value = $data['icon']['value'];
196
197 // If it's a URL, try to find the attachment ID
198 if (filter_var($value, FILTER_VALIDATE_URL)) {
199 $attachment_id = attachment_url_to_postid($value);
200 if ($attachment_id) {
201 $data['icon']['value'] = $attachment_id;
202 }
203 }
204 // If it's already numeric, keep it as is
205 elseif (is_numeric($value)) {
206 $data['icon']['value'] = (int) $value;
207 }
208 }
209
210 $data['icon'] = maybe_serialize($data['icon']);
211 }
212 }
213
214 // Handle SEO metadata
215 if (isset($data['seo_title']) || isset($data['seo_description']) || isset($data['seo_keywords'])) {
216 // Get existing metadata
217 $existing_metadata = [];
218 if (isset($data['metadata']) && is_array($data['metadata'])) {
219 $existing_metadata = $data['metadata'];
220 } elseif (isset($data['metadata'])) {
221 $existing_metadata = maybe_unserialize($data['metadata']);
222 } else {
223 // Get existing metadata from database
224 $existing = $this->repository->find($id);
225 if ($existing && isset($existing->metadata)) {
226 $existing_metadata = maybe_unserialize($existing->metadata);
227 }
228 }
229
230 // Add/update SEO fields in metadata
231 if (isset($data['seo_title'])) {
232 $existing_metadata['seo_title'] = sanitize_text_field($data['seo_title']);
233 unset($data['seo_title']); // Remove from main data
234 }
235 if (isset($data['seo_description'])) {
236 $existing_metadata['seo_description'] = sanitize_textarea_field($data['seo_description']);
237 unset($data['seo_description']); // Remove from main data
238 }
239 if (isset($data['seo_keywords'])) {
240 $existing_metadata['seo_keywords'] = sanitize_text_field($data['seo_keywords']);
241 unset($data['seo_keywords']); // Remove from main data
242 }
243
244 $data['metadata'] = $existing_metadata;
245 }
246
247 // Sanitize metadata if it's an array
248 if (isset($data['metadata'])) {
249 if (is_array($data['metadata'])) {
250 $data['metadata'] = maybe_serialize($data['metadata']);
251 }
252 }
253
254 return $data;
255 }
256
257 /**
258 * Get all categories
259 */
260 public function getAll(array $args = []): array
261 {
262 // IMPORTANT: Always filter by type = 'category' for categories
263 $args['where']['type'] = 'category';
264
265 // Sanitize and handle search
266 if (!empty($args['search'])) {
267 $search = sanitize_text_field($args['search']);
268 return $this->repository->search($search, $args);
269 }
270
271 // Sanitize and handle status filter
272 if (!empty($args['status']) && $args['status'] !== 'all') {
273 $allowed_statuses = ['draft', 'publish', 'trash'];
274 $status = in_array($args['status'], $allowed_statuses, true)
275 ? $args['status']
276 : null;
277 if ($status) {
278 $args['where']['status'] = $status;
279 }
280 }
281
282
283 $result = $this->repository->all($args);
284
285
286 return $result;
287 }
288
289 /**
290 * Count items
291 */
292 public function count(array $args = []): int
293 {
294 // IMPORTANT: Always filter by type = 'category' for categories
295 $args['where']['type'] = 'category';
296
297 // Sanitize and handle search
298 if (!empty($args['search'])) {
299 $search = sanitize_text_field($args['search']);
300 $items = $this->repository->search($search, $args);
301 return count($items);
302 }
303
304 // Sanitize and handle status filter
305 if (!empty($args['status']) && $args['status'] !== 'all') {
306 $allowed_statuses = ['draft', 'publish', 'trash'];
307 $status = in_array($args['status'], $allowed_statuses, true)
308 ? $args['status']
309 : null;
310 if ($status) {
311 $args['where']['status'] = $status;
312 }
313 }
314
315 return $this->repository->count($args);
316 }
317
318 /**
319 * Get published categories with stats
320 */
321 public function getPublishedWithStats(): array
322 {
323 // For now this simply delegates to a repository method that attaches
324 // trips_count using the yatra_trip_classifications relation table.
325 return $this->repository->getPublishedWithTripCounts();
326 }
327
328 /**
329 * Get status counts
330 */
331 public function getStatusCounts(): array
332 {
333 $counts = $this->repository->getStatusCounts();
334
335 $publish = $counts['publish'] ?? 0;
336 $draft = $counts['draft'] ?? 0;
337 $trash = $counts['trash'] ?? 0;
338
339 // Calculate total from all repository total count, not sum of all values
340 $all = $counts['total'] ?? 0;
341
342 $result = [
343 'all' => (int) $all,
344 'publish' => (int) $publish,
345 'draft' => (int) $draft,
346 'trash' => (int) $trash,
347 ];
348
349 // Add legacy status keys for backward compatibility
350 $result['active'] = $result['publish'];
351 $result['inactive'] = $result['trash'];
352
353 return $result;
354 }
355
356 /**
357 * Override update method to add debugging
358 */
359 public function update(int $id, array $data): bool
360 {
361 try {
362 return parent::update($id, $data);
363 } catch (\Exception $e) {
364 // DEBUG: Log the error
365 if (defined('WP_DEBUG') && WP_DEBUG) {
366 }
367 throw $e;
368 }
369 }
370
371 /**
372 * Get trip count for a category
373 */
374 public function getTripCount(int $categoryId): int
375 {
376 $categoryRepository = new \Yatra\Repositories\CategoryRepository();
377 return $categoryRepository->getTripCount($categoryId);
378 }
379
380 /**
381 * Get trip count for category (direct field method)
382 */
383 public function getTripCountDirect(int $categoryId): int
384 {
385 $categoryRepository = new \Yatra\Repositories\CategoryRepository();
386 return $categoryRepository->getTripCountDirect($categoryId);
387 }
388
389 /**
390 * Get subcategories by parent ID
391 */
392 public function getSubcategories(int $parentId, array $args = []): array
393 {
394 return $this->repository->getSubcategories($parentId, $args);
395 }
396
397 /**
398 * Get all categories with subcategories (hierarchical)
399 */
400 public function getHierarchical(array $args = []): array
401 {
402 return $this->repository->getHierarchical($args);
403 }
404 }
405