repository = new ItemTypeRepository(); } /** * Get repository */ protected function getRepository(): ItemTypeRepository { return $this->repository; } /** * Get repository (public access for controllers) */ public function getRepositoryInstance(): ItemTypeRepository { return $this->repository; } /** * Validate item type data */ protected function validate(array $data, ?int $id = null): void { if (empty($data['name'])) { throw new \InvalidArgumentException('Item type name is required'); } // Slug will be auto-generated from name, so we don't need to validate it here // The SlugHelper will ensure uniqueness // Validate status $allowed_statuses = ['draft', 'publish', 'trash']; if (isset($data['status']) && !in_array($data['status'], $allowed_statuses, true)) { throw new \InvalidArgumentException('Invalid status. Must be one of: ' . implode(', ', $allowed_statuses)); } // Validate color $allowed_colors = ['blue', 'green', 'purple', 'orange', 'red', 'yellow', 'gray']; if (isset($data['color']) && !in_array($data['color'], $allowed_colors, true)) { throw new \InvalidArgumentException('Invalid color. Must be one of: ' . implode(', ', $allowed_colors)); } } /** * Process before create */ protected function processBeforeCreate(array $data): array { // Set type to item_type for ClassificationsTable $data['type'] = \Yatra\Constants\ClassificationTypes::ITEM_TYPE; // Sanitize name if (isset($data['name'])) { $data['name'] = sanitize_text_field($data['name']); } // Always auto-generate slug from name (backend ensures uniqueness) if (!empty($data['name'])) { // Use ClassificationsTable directly to avoid protected method issue $tableName = \Yatra\Database\Tables\ClassificationsTable::getTableName(); // Remove WordPress prefix since SlugHelper adds it automatically global $wpdb; $tableNameWithoutPrefix = str_replace($wpdb->prefix, '', $tableName); $data['slug'] = SlugHelper::generateUniqueFromDatabase( $data['name'], $tableNameWithoutPrefix, 'slug' ); } elseif (isset($data['slug'])) { // If name is empty but slug is provided, sanitize it $data['slug'] = SlugHelper::generate($data['slug']); } // Sanitize description if (isset($data['description'])) { $data['description'] = sanitize_textarea_field($data['description']); } // Sanitize status if (isset($data['status'])) { $allowed_statuses = ['draft', 'publish', 'trash']; $data['status'] = in_array($data['status'], $allowed_statuses, true) ? $data['status'] : 'draft'; } else { $data['status'] = 'draft'; } // Sanitize color if (isset($data['color'])) { $allowed_colors = ['blue', 'green', 'purple', 'orange', 'red', 'yellow', 'gray']; $data['color'] = in_array($data['color'], $allowed_colors, true) ? $data['color'] : 'blue'; } else { $data['color'] = 'blue'; } // Set created_by and updated_by to current user $current_user_id = get_current_user_id(); $data['created_by'] = absint($current_user_id); $data['updated_by'] = absint($current_user_id); // Sanitize and serialize icon if it's an array if (isset($data['icon'])) { if (is_array($data['icon'])) { $data['icon'] = yatra_normalize_icon_picker_for_storage($data['icon']); $data['icon'] = maybe_serialize($data['icon']); } elseif (is_string($data['icon'])) { // If it's already a string, sanitize it $data['icon'] = sanitize_text_field($data['icon']); } } return $data; } /** * Process before update */ protected function processBeforeUpdate(int $id, array $data): array { // Sanitize name if (isset($data['name'])) { $data['name'] = sanitize_text_field($data['name']); } // Handle slug: preserve manually edited slug, otherwise auto-generate from name $preserveSlug = isset($data['preserve_slug']) && $data['preserve_slug'] === true; unset($data['preserve_slug']); // Remove flag from data array if ($preserveSlug && isset($data['slug']) && !empty($data['slug'])) { // Slug was manually edited - preserve it but ensure uniqueness $data['slug'] = SlugHelper::generateUniqueFromDatabase( $data['slug'], 'yatra_item_types', 'slug', $id // Exclude current record when checking uniqueness ); } elseif (!empty($data['name'])) { // Auto-generate slug from name if name is provided and slug not manually edited $data['slug'] = SlugHelper::generateUniqueFromDatabase( $data['name'], 'yatra_item_types', 'slug', $id // Exclude current record when checking uniqueness ); } elseif (isset($data['slug'])) { // If name is not provided but slug is, sanitize the slug $data['slug'] = SlugHelper::generate($data['slug']); } // Sanitize description if (isset($data['description'])) { $data['description'] = sanitize_textarea_field($data['description']); } // Sanitize status if (isset($data['status'])) { $allowed_statuses = ['draft', 'publish', 'trash']; $data['status'] = in_array($data['status'], $allowed_statuses, true) ? $data['status'] : 'draft'; } // Sanitize color if (isset($data['color'])) { $allowed_colors = ['blue', 'green', 'purple', 'orange', 'red', 'yellow', 'gray']; $data['color'] = in_array($data['color'], $allowed_colors, true) ? $data['color'] : 'blue'; } // Set updated_by to current user $data['updated_by'] = absint(get_current_user_id()); // Sanitize and serialize icon if it's an array if (isset($data['icon'])) { if (is_array($data['icon'])) { $data['icon'] = yatra_normalize_icon_picker_for_storage($data['icon']); $data['icon'] = maybe_serialize($data['icon']); } elseif (is_string($data['icon'])) { // If it's already a string, sanitize it $data['icon'] = sanitize_text_field($data['icon']); } } return $data; } /** * Get all items with search and filters */ public function getAll(array $args = []): array { // Sanitize and handle search if (!empty($args['search'])) { $search = sanitize_text_field($args['search']); return $this->repository->search($search, $args); } // Sanitize and handle status filter if (!empty($args['status']) && $args['status'] !== 'all') { $allowed_statuses = ['draft', 'publish', 'trash']; $status = in_array($args['status'], $allowed_statuses, true) ? $args['status'] : null; if ($status) { $args['where']['status'] = $status; } } return $this->repository->all($args); } /** * Get published item types */ public function getPublished(array $args = []): array { return $this->repository->getPublished($args); } /** * Count items */ public function count(array $args = []): int { $args['where']['type']='item_type'; // Sanitize and handle search if (!empty($args['search'])) { $search = sanitize_text_field($args['search']); $items = $this->repository->search($search, $args); return count($items); } // Sanitize and handle status filter if (!empty($args['status']) && $args['status'] !== 'all') { $allowed_statuses = ['draft', 'publish', 'trash']; $status = in_array($args['status'], $allowed_statuses, true) ? $args['status'] : null; if ($status) { $args['where']['status'] = $status; } } return $this->repository->count($args); } /** * Get status counts for admin list views * * Provides stable counts for All / Published / Draft / Trash that * do not change when filters (status/search) are applied in the UI. */ public function getStatusCounts(): array { // All statuses combined (no status filter) $all = $this->count([]); // Individual statuses $publish = $this->count(['status' => 'publish']); $draft = $this->count(['status' => 'draft']); $trash = $this->count(['status' => 'trash']); return [ 'all' => (int) $all, 'publish' => (int) $publish, 'draft' => (int) $draft, 'trash' => (int) $trash, ]; } }