post_type ) { return null; } return $this->post_to_dto( $post ); } /** * Find a peptide by slug. * * @param string $slug Post slug. * @return PR_Core_Peptide_DTO|null */ public function find_by_slug( string $slug ): ?PR_Core_Peptide_DTO { $posts = get_posts( [ 'post_type' => PR_Core_Peptide_CPT::POST_TYPE, 'name' => sanitize_title( $slug ), 'posts_per_page' => 1, 'post_status' => 'any', ] ); return ! empty( $posts ) ? $this->post_to_dto( $posts[0] ) : null; } /** * Search peptides by name or alias. * * @param string $query Search term. * @param int $limit Max results (default 20). * @return PR_Core_Peptide_DTO[] */ public function search( string $query, int $limit = 20 ): array { $query = sanitize_text_field( $query ); $posts = get_posts( [ 'post_type' => PR_Core_Peptide_CPT::POST_TYPE, 's' => $query, 'posts_per_page' => $limit, 'post_status' => 'publish', ] ); return array_map( [ $this, 'post_to_dto' ], $posts ); } /** * Find all peptides matching filters. * * @param array $filters Supported: status, category, family, * evidence_strength, per_page, page. * @return PR_Core_Peptide_DTO[] */ public function find_all( array $filters = [] ): array { $args = [ 'post_type' => PR_Core_Peptide_CPT::POST_TYPE, 'posts_per_page' => (int) ( $filters['per_page'] ?? 100 ), 'paged' => (int) ( $filters['page'] ?? 1 ), 'orderby' => 'title', 'order' => 'ASC', ]; $args['post_status'] = $filters['status'] ?? 'publish'; if ( ! empty( $filters['category'] ) ) { $args['tax_query'] = [ [ 'taxonomy' => PR_Core_Peptide_CPT::TAX_CATEGORY, 'field' => 'slug', 'terms' => sanitize_text_field( $filters['category'] ), ] ]; } // v0.2.0: `family` filter silently ignored — `pr_peptide_family` taxonomy // was removed with the CPT consolidation. Callers passing `family` get // all-category results rather than a fatal. Remove this key from the // REST schema in a future minor bump. if ( ! empty( $filters['evidence_strength'] ) ) { $args['meta_query'] = [ [ 'key' => 'evidence_strength', 'value' => sanitize_text_field( $filters['evidence_strength'] ), ] ]; } $posts = get_posts( $args ); return array_map( [ $this, 'post_to_dto' ], $posts ); } /** * Count total peptides matching a status. * * @param string $status Post status (default 'publish'). * @return int */ public function count( string $status = 'publish' ): int { $counts = wp_count_posts( PR_Core_Peptide_CPT::POST_TYPE ); return (int) ( $counts->$status ?? 0 ); } /** * Convert a WP_Post to a Peptide DTO. * * @param \WP_Post $post WordPress post object. * @return PR_Core_Peptide_DTO */ private function post_to_dto( \WP_Post $post ): PR_Core_Peptide_DTO { $aliases_raw = get_post_meta( $post->ID, 'aliases', true ); $aliases = json_decode( $aliases_raw ?: '[]', true ) ?: []; $categories = wp_get_post_terms( $post->ID, PR_Core_Peptide_CPT::TAX_CATEGORY, [ 'fields' => 'names' ] ); // v0.2.0: `pr_peptide_family` taxonomy removed. DTO `families` field // preserved as empty array to keep the REST response shape stable for // clients; will be dropped in a future minor bump with a release note. $families = []; return new PR_Core_Peptide_DTO( [ 'id' => $post->ID, 'title' => $post->post_title, 'slug' => $post->post_name, 'content' => $post->post_content, 'excerpt' => $post->post_excerpt, 'status' => $post->post_status, 'display_name' => get_post_meta( $post->ID, 'display_name', true ) ?: '', 'aliases' => $aliases, 'molecular_formula' => get_post_meta( $post->ID, 'molecular_formula', true ) ?: '', 'molecular_weight' => (float) get_post_meta( $post->ID, 'molecular_weight', true ), 'cas_number' => get_post_meta( $post->ID, 'cas_number', true ) ?: '', 'drugbank_id' => get_post_meta( $post->ID, 'drugbank_id', true ) ?: '', 'chembl_id' => get_post_meta( $post->ID, 'chembl_id', true ) ?: '', 'evidence_strength' => get_post_meta( $post->ID, 'evidence_strength', true ) ?: 'preclinical', 'editorial_review_status' => get_post_meta( $post->ID, 'editorial_review_status', true ) ?: 'draft', 'last_editorial_review_at' => get_post_meta( $post->ID, 'last_editorial_review_at', true ) ?: '', 'medical_editor_id' => (int) get_post_meta( $post->ID, 'medical_editor_id', true ), 'categories' => is_array( $categories ) ? $categories : [], 'families' => $families, ] ); } }