WP_REST_Server::READABLE, 'callback' => [ $this, 'handle_search_request' ], 'permission_callback' => function() { return current_user_can( adpresso_get_capability( 'edit_ad' ) ); }, 'args' => [ 's' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ], ], ] ); } /** * Handles the search request and returns the JSON response. * * @param WP_REST_Request $request The REST request object. * * @return WP_REST_Response|WP_Error */ public function handle_search_request( WP_REST_Request $request ): WP_Error|WP_REST_Response { $search_term = $request->get_param( 's' ); if ( mb_strlen( $search_term ) < self::MIN_SEARCH_LENGTH ) { return rest_ensure_response( [] ); } $results = []; $results = array_merge( $results, $this->search_entities( $search_term ) ); $results = array_merge( $results, $this->search_settings( $search_term ) ); $unique_results = []; $seen_urls = []; foreach ( $results as $result ) { if ( isset( $result['url'] ) && ! in_array( $result['url'], $seen_urls, true ) ) { $unique_results[] = $result; $seen_urls[] = $result['url']; } } $results = array_slice( $unique_results, 0, 40 ); return rest_ensure_response( $results ); } /** * Helper function to check if a string contains all words from a search term. * * @param string $haystack The string to search within. * @param string $search_term The string of keywords to find. * * @return bool True if all keywords are found. */ private function string_contains_all_keywords( string $haystack, string $search_term ): bool { // Split the search term into individual keywords $keywords = explode( ' ', $search_term ); // Remove empty values that can result from multiple spaces $keywords = array_filter( $keywords, 'trim' ); if ( empty( $keywords ) ) { return false; } // Check if all keywords exist in the haystack (case-insensitive) foreach ( $keywords as $keyword ) { if ( stripos( $haystack, $keyword ) === false ) { return false; // If one keyword is not found, it's not a match } } return true; // All keywords were found } /** * Searches through all loaded Ads, Groups, and Placements. * * @param string $search_term The user's search term. * * @return array A list of formatted result items. */ private function search_entities( string $search_term ): array { $found_items = []; $placements = adpresso_get_placement_repository()->get_all(); $groups = adpresso_get_group_repository()->get_all(); $ads = adpresso_get_ad_repository()->get_all(); $all_entities = array_merge( $ads, $groups, $placements ); foreach ( $all_entities as $entity ) { /** @var Entity $entity */ $title = $entity->get_title(); $context = ''; if ( $entity instanceof Ad ) { $type_label = __( 'Ad', 'adpresso' ); $type_slug = 'ad'; $list_slug = 'ads'; $context = $entity->get_type_object() ? $entity->get_type_object()->get_title() : $entity->get_type(); } elseif ( $entity instanceof Group ) { $type_label = __( 'Group', 'adpresso' ); $type_slug = 'group'; $list_slug = 'groups'; } elseif ( $entity instanceof Placement ) { $type_label = __( 'Placement', 'adpresso' ); $type_slug = 'placement'; $list_slug = 'placements'; $context = $entity->get_type_object() ? $entity->get_type_object()->get_title() : $entity->get_type(); } else { continue; } $url = Util::get_entity_edit_url( $list_slug, $entity->get_id() ); $searchable_string = $title . ' ' . $context; if ( $this->string_contains_all_keywords( $searchable_string, $search_term ) ) { $found_items[] = [ 'title' => $title, 'id' => $entity->get_id(), 'type' => $type_label, 'type_slug' => $type_slug, 'list_slug' => $list_slug, 'context' => $context, 'description' => '', 'url' => $url, ]; } // Search in title and context if ( stripos( $title, $search_term ) !== false ) { $found_items[] = [ 'title' => $title, 'type' => $type_label, 'type_slug' => $type_slug, 'context' => $context, 'url' => $url, ]; } } return $found_items; } /** * Searches through all registered setting definitions. * * @param string $search_term The user's search term. * * @return array A list of formatted result items. */ private function search_settings( string $search_term ): array { $found_items = []; $settings_page = SettingsPage::get(); foreach ( $settings_page->get_tabs() as $tab ) { $tab_id = $tab->get_id(); $tab_title = $tab->get_title(); foreach ( $tab->get_options() as $option_key => $option_config ) { $option_label = $option_config['label'] ?? ''; $option_desc = $option_config['description'] ?? ''; $searchable_string = $option_label . ' ' . $option_desc; if ( $this->string_contains_all_keywords( $searchable_string, $search_term ) ) { $url = add_query_arg( [ 'page' => 'adpresso_settings', 'tab' => $tab_id ], admin_url( 'admin.php' ) ); // the anchor should point to the field ID, e.g. '#adpresso_ad_label_text' $url .= '#' . ( Constants::META_PREFIX ?? 'adpresso_' ) . 'wrapper_' . $option_key; $found_items[] = [ 'title' => $option_label, 'type' => __( 'Setting', 'adpresso' ), 'type_slug' => 'setting', 'context' => $tab_title, 'description' => $option_desc, 'url' => $url, ]; } } } return $found_items; } }