PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.7
Yatra – Travel Booking & Tour Operator Software v3.0.7
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 2.0.11 All 82 releases
yatra / app / Services / ItemService.php

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

300 lines 9.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\Repositories\ItemRepository;
8 use Yatra\Repositories\ItemTypeRepository;
9 use Yatra\Helpers\SlugHelper;
10
11 /**
12 * Item Service
13 * Contains business logic for items (item subtypes)
14 */
15 class ItemService extends BaseService
16 {
17 /**
18 * @var ItemRepository
19 */
20 private ItemRepository $repository;
21
22 /**
23 * @var ItemTypeRepository
24 */
25 private ItemTypeRepository $itemTypeRepository;
26
27 /**
28 * Constructor
29 */
30 public function __construct()
31 {
32 $this->repository = new ItemRepository();
33 $this->itemTypeRepository = new ItemTypeRepository();
34 }
35
36 /**
37 * Get repository
38 */
39 protected function getRepository(): ItemRepository
40 {
41 return $this->repository;
42 }
43
44 /**
45 * Validate item data
46 */
47 protected function validate(array $data, ?int $id = null): void
48 {
49 if (empty($data['name'])) {
50 throw new \InvalidArgumentException('Item name is required');
51 }
52
53 if (empty($data['type_id'])) {
54 throw new \InvalidArgumentException('Item type is required');
55 }
56
57 // Validate type_id exists
58 $itemType = $this->itemTypeRepository->find((int) $data['type_id']);
59 if (!$itemType) {
60 throw new \InvalidArgumentException('Invalid item type selected');
61 }
62
63 // Validate status
64 $allowed_statuses = ['draft', 'publish', 'trash'];
65 if (isset($data['status']) && !in_array($data['status'], $allowed_statuses, true)) {
66 throw new \InvalidArgumentException('Invalid status. Must be one of: ' . implode(', ', $allowed_statuses));
67 }
68 }
69
70 /**
71 * Process before create
72 */
73 protected function processBeforeCreate(array $data): array
74 {
75 // DEBUG: Log incoming data
76 if (defined('WP_DEBUG') && WP_DEBUG) {
77 }
78
79 // Set type to item for ClassificationsTable
80 $data['type'] = 'item';
81
82 // Sanitize name
83 if (isset($data['name'])) {
84 $data['name'] = sanitize_text_field($data['name']);
85 }
86
87 // Always auto-generate slug from name (backend ensures uniqueness)
88 if (!empty($data['name'])) {
89 // Use ClassificationsTable directly to avoid protected method issue
90 $tableName = \Yatra\Database\Tables\ClassificationsTable::getTableName();
91 // Remove WordPress prefix since SlugHelper adds it automatically
92 global $wpdb;
93 $tableNameWithoutPrefix = str_replace($wpdb->prefix, '', $tableName);
94
95 $data['slug'] = SlugHelper::generateUniqueFromDatabase(
96 $data['name'],
97 $tableNameWithoutPrefix,
98 'slug'
99 );
100 } elseif (isset($data['slug'])) {
101 // If name is empty but slug is provided, sanitize it
102 $data['slug'] = SlugHelper::generate($data['slug']);
103 }
104
105 // Sanitize description
106 if (isset($data['description'])) {
107 $data['description'] = sanitize_textarea_field($data['description']);
108 }
109
110 // Store type_id in parent_id column (ClassificationsTable uses parent_id for item type relationship)
111 if (isset($data['type_id'])) {
112 $data['parent_id'] = absint($data['type_id']);
113 unset($data['type_id']); // Remove from main data
114 }
115
116 // Sanitize status
117 if (isset($data['status'])) {
118 $allowed_statuses = ['draft', 'publish', 'trash'];
119 $data['status'] = in_array($data['status'], $allowed_statuses, true)
120 ? $data['status']
121 : 'draft';
122 } else {
123 $data['status'] = 'draft';
124 }
125
126 // Set created_by and updated_by to current user
127 $current_user_id = get_current_user_id();
128 $data['created_by'] = absint($current_user_id);
129 $data['updated_by'] = absint($current_user_id);
130
131 return $data;
132 }
133
134 /**
135 * Process before update
136 */
137 protected function processBeforeUpdate(int $id, array $data): array
138 {
139 // Ensure type is not changed from item
140 $data['type'] = 'item';
141
142 // Sanitize name
143 if (isset($data['name'])) {
144 $data['name'] = sanitize_text_field($data['name']);
145 }
146
147 // Handle slug: preserve manually edited slug, otherwise auto-generate from name
148 $preserveSlug = isset($data['preserve_slug']) && $data['preserve_slug'] === true;
149 unset($data['preserve_slug']); // Remove flag from data array
150
151 if ($preserveSlug && isset($data['slug']) && !empty($data['slug'])) {
152 // Slug was manually edited - preserve it but ensure uniqueness
153 $data['slug'] = SlugHelper::generateUniqueFromDatabase(
154 $data['slug'],
155 'yatra_classifications',
156 'slug',
157 $id // Exclude current record when checking uniqueness
158 );
159 } elseif (!empty($data['name'])) {
160 // Auto-generate slug from name if name is provided and slug not manually edited
161 $data['slug'] = SlugHelper::generateUniqueFromDatabase(
162 $data['name'],
163 'yatra_classifications',
164 'slug',
165 $id // Exclude current record when checking uniqueness
166 );
167 } elseif (isset($data['slug'])) {
168 // If name is not provided but slug is, sanitize the slug
169 $data['slug'] = SlugHelper::generate($data['slug']);
170 }
171
172 // Sanitize description
173 if (isset($data['description'])) {
174 $data['description'] = sanitize_textarea_field($data['description']);
175 }
176
177 // Store type_id in parent_id column (ClassificationsTable uses parent_id for item type relationship)
178 if (isset($data['type_id'])) {
179 $data['parent_id'] = absint($data['type_id']);
180 unset($data['type_id']); // Remove from main data
181 }
182
183 // Sanitize status
184 if (isset($data['status'])) {
185 $allowed_statuses = ['draft', 'publish', 'trash'];
186 $data['status'] = in_array($data['status'], $allowed_statuses, true)
187 ? $data['status']
188 : 'draft';
189 }
190
191 // Set updated_by to current user
192 $data['updated_by'] = absint(get_current_user_id());
193
194 return $data;
195 }
196
197 /**
198 * Get all items with search and filters
199 */
200 public function getAll(array $args = []): array
201 {
202 // Sanitize and handle search
203 if (!empty($args['search'])) {
204 $search = sanitize_text_field($args['search']);
205 return $this->repository->search($search, $args);
206 }
207
208 // Always filter by type = 'item' for items
209 $args['where']['type'] = 'item';
210
211 // Sanitize and handle type filter
212 if (!empty($args['type_id']) && $args['type_id'] !== 'all') {
213 $type_id = absint($args['type_id']);
214 if ($type_id > 0) {
215 $args['where']['parent_id'] = $type_id;
216 }
217 }
218
219 // Sanitize and handle status filter
220 if (!empty($args['status']) && $args['status'] !== 'all') {
221 $allowed_statuses = ['draft', 'publish', 'trash'];
222 $status = in_array($args['status'], $allowed_statuses, true)
223 ? $args['status']
224 : null;
225 if ($status) {
226 $args['where']['status'] = $status;
227 }
228 }
229
230 return $this->repository->all($args);
231 }
232
233 /**
234 * Get published items
235 */
236 public function getPublished(array $args = []): array
237 {
238 return $this->repository->getPublished($args);
239 }
240
241 /**
242 * Count items
243 */
244 public function count(array $args = []): int
245 {
246 // Sanitize and handle search
247 if (!empty($args['search'])) {
248 $search = sanitize_text_field($args['search']);
249 $items = $this->repository->search($search, $args);
250 return count($items);
251 }
252
253 // Sanitize and handle type filter
254 if (!empty($args['type_id']) && $args['type_id'] !== 'all') {
255 $type_id = absint($args['type_id']);
256 if ($type_id > 0) {
257 $args['where']['parent_id'] = $type_id;
258 }
259 }
260
261 // Sanitize and handle status filter
262 if (!empty($args['status']) && $args['status'] !== 'all') {
263 $allowed_statuses = ['draft', 'publish', 'trash'];
264 $status = in_array($args['status'], $allowed_statuses, true)
265 ? $args['status']
266 : null;
267 if ($status) {
268 $args['where']['status'] = $status;
269 }
270 }
271
272 return $this->repository->count($args);
273 }
274
275 /**
276 * Get status counts for admin list views
277 *
278 * Provides stable counts for All / Published / Draft / Trash that
279 * do not change when filters (status/search/type) are applied in the UI.
280 */
281 public function getStatusCounts(): array
282 {
283 // All statuses combined (no status filter)
284 $all = $this->count([]);
285
286 // Individual statuses
287 $publish = $this->count(['status' => 'publish']);
288 $draft = $this->count(['status' => 'draft']);
289 $trash = $this->count(['status' => 'trash']);
290
291 return [
292 'all' => (int) $all,
293 'publish' => (int) $publish,
294 'draft' => (int) $draft,
295 'trash' => (int) $trash,
296 ];
297 }
298 }
299
300