PHP code example of considbrs-webdev / typesense-search

1. Go to this page and download the library: Download considbrs-webdev/typesense-search library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

considbrs-webdev / typesense-search example snippets



// wp-content/config/typesense.php
define('TYPESENSE_HOST',       'https://search.example.com');
define('TYPESENSE_COLLECTION', 'my-wordpress-site');
define('TYPESENSE_ADMIN_KEY',  'your-admin-key');
define('TYPESENSE_SEARCH_KEY', 'your-search-only-key');
// define('TYPESENSE_FRONTEND_HOST', 'https://public.example.com'); // optional

add_action(
    'Municipio/TypesenseSearch/RegisterStrategies',
    function (
        \TypesenseSearch\Indexing\IndexingRegistry $registry,
        \TypesenseSearch\Services\TypesenseClientService $clientService,
        \TypesenseSearch\Services\SettingsRepository $settings,
        \TypesenseSearch\Logger\LoggerInterface $logger
    ): void {
        $registry->register(new MyCustomStrategy($clientService, $settings, new MySentryLogger()));
    },
    10, 4
);

// Re-index a post after your plugin changes data that affects the index
do_action('typesense_search/index_post', $post_id);

// Explicitly remove a post from the index
do_action('typesense_search/deindex_post', $post_id);

$doc = new IndexableDocument([
    'id'    => (string) $post->ID,
    'title' => $post->post_title,
    'url'   => get_permalink($post),
    // ...
]);

// Non-destructive update (returns a new instance)
$doc = $doc->with('author', get_the_author());

// Pass to Typesense
$doc->toArray();

// Add a field to every indexed post
add_filter(
    'Municipio/TypesenseSearch/DocumentBuilder/build',
    function (array $document, \WP_Post $post): array {
        $document['author'] = get_the_author_meta('display_name', $post->post_author);
        return $document;
    },
    10,
    2
);

// Add a field only to documents of post_type "event"
add_filter(
    'Municipio/TypesenseSearch/DocumentBuilder/event/build',
    function (array $document, \WP_Post $post): array {
        $document['event_date'] = get_post_meta($post->ID, '_event_date', true);
        return $document;
    },
    10,
    2
);

namespace MyPlugin\Search;

use TypesenseSearch\Indexing\IndexableDocument;
use TypesenseSearch\Indexing\Strategies\AbstractIndexingStrategy;

class ProductIndexingStrategy extends AbstractIndexingStrategy
{
    public function getIdentifier(): string
    {
        return 'product';
    }

    public function supports(\WP_Post $post): bool
    {
        return $post->post_type === 'product';
    }

    public function shouldIndex(\WP_Post $post): bool
    {
        // Only index products that are in stock
        return $post->post_status === 'publish'
            && get_post_meta($post->ID, '_stock_status', true) === 'instock';
    }

    public function buildDocument(\WP_Post $post): IndexableDocument|false
    {
        $price = get_post_meta($post->ID, '_price', true);
        if ($price === '') {
            return false; // skip products without a price
        }

        return new IndexableDocument([
            'id'       => (string) $post->ID,
            'title'    => $post->post_title,
            'content'  => wp_strip_all_tags($post->post_content),
            'excerpt'  => get_the_excerpt($post),
            'url'      => get_permalink($post),
            'type'     => 'product',
            'type_name'=> __('Product', 'my-plugin'),
            'price'    => (float) $price,
        ]);
    }
}

add_action(
    'Municipio/TypesenseSearch/RegisterStrategies',
    function (
        \TypesenseSearch\Indexing\IndexingRegistry $registry,
        \TypesenseSearch\Services\TypesenseClientService $clientService,
        \TypesenseSearch\Services\SettingsRepository $settings,
        \TypesenseSearch\Logger\LoggerInterface $logger
    ): void {
        // Register before PostIndexingStrategy if your type could otherwise
        // be caught by the generic post handler first.
        $registry->register(new \MyPlugin\Search\ProductIndexingStrategy($clientService, $settings, $logger));
    },
    10, 4
);

// Prevent a specific post from being indexed without touching the meta box
add_filter(
    \TypesenseSearch\Indexing\Strategies\PostIndexingStrategy::FILTER_SHOULD_INDEX,
    function (bool $shouldIndex, \WP_Post $post): bool {
        if ($post->post_type === 'post' && has_tag('no-index', $post)) {
            return false;
        }
        return $shouldIndex;
    },
    10,
    2
);

namespace MyPlugin\Search;

use TypesenseSearch\Indexing\IndexableDocument;
use TypesenseSearch\Indexing\Strategies\AbstractExternalIndexingStrategy;

class EServiceIndexingStrategy extends AbstractExternalIndexingStrategy
{
    public const CRON_HOOK = 'myplugin_sync_eservices';

    // ── Identity ────────────────────────────────────────────────────────────

    public function getIdentifier(): string
    {
        return 'eservice';
    }

    // ── Hook registration ───────────────────────────────────────────────────

    /**
     * Schedule a daily WP-Cron sync and wire it to syncAll().
     * Called automatically by IndexingRegistry::registerAllHooks().
     */
    public function registerHooks(): void
    {
        add_action('init', function (): void {
            if (!wp_next_scheduled(self::CRON_HOOK)) {
                wp_schedule_event(time(), 'daily', self::CRON_HOOK);
            }
        });

        add_action(self::CRON_HOOK, [$this, 'syncAll']);
    }

    // ── Data fetching ───────────────────────────────────────────────────────

    /**
     * Fetch all items from the external source.
     * May return any iterable — array, Generator, or Traversable.
     */
    protected function fetchItems(): iterable
    {
        $response = wp_remote_get('https://api.example.com/eservices', ['timeout' => 15]);

        if (is_wp_error($response)) {
            $this->logger->error('[EService] API error: ' . $response->get_error_message());
            return [];
        }

        $data = json_decode(wp_remote_retrieve_body($response), true);

        return $data['items'] ?? [];
    }

    // ── Document building ───────────────────────────────────────────────────

    /**
     * Convert one raw item into an IndexableDocument.
     * Return false to skip the item.
     */
    protected function buildDocument(mixed $item): IndexableDocument|false
    {
        if (empty($item['id']) || empty($item['title'])) {
            return false;
        }

        return new IndexableDocument([
            'id'        => $this->getExternalId($item),   // MUST be namespaced
            'title'     => (string) $item['title'],
            'content'   => (string) ($item['description'] ?? ''),
            'excerpt'   => (string) ($item['short_description'] ?? ''),
            'url'       => (string) ($item['url'] ?? ''),
            'type'      => 'eservice',
            'type_name' => __('E-service', 'my-plugin'),
            'date'      => isset($item['updated_at'])
                               ? (int) strtotime($item['updated_at'])
                               : 0,
        ]);
    }

    /**
     * Return the namespaced Typesense document ID for a raw item.
     * Must match the 'id' value set in buildDocument().
     */
    protected function getExternalId(mixed $item): string
    {
        return 'eservice-' . $item['id'];
    }
}

add_action(
    'Municipio/TypesenseSearch/RegisterStrategies',
    function (
        \TypesenseSearch\Indexing\IndexingRegistry $registry,
        \TypesenseSearch\Services\TypesenseClientService $clientService,
        \TypesenseSearch\Services\SettingsRepository $settings,
        \TypesenseSearch\Logger\LoggerInterface $logger
    ): void {
        $registry->registerExternal(new \MyPlugin\Search\EServiceIndexingStrategy($clientService, $settings, $logger));
    },
    10, 4
);

$registry = \TypesenseSearch\App::getRegistry();

// Sync one strategy
$count = $registry->runExternalSync('eservice');  // returns items indexed

// Sync all external strategies
$results = $registry->runAllExternalSyncs();  // ['eservice' => 42, ...]

$registry->getExternal('eservice')->deindex('eservice-42');

add_filter(
    'Municipio/TypesenseSearch/placeholderMappings',
    function (array $mappings): array {
        // {SEARCH_HIT_DEPARTMENT} will be replaced with the value of the
        // 'department' field on each Typesense hit document.
        $mappings['SEARCH_HIT_DEPARTMENT'] = 'department';
        return $mappings;
    }
);

add_filter(
    'Municipio/TypesenseSearch/postTypeToTemplate',
    function (array $mapping): array {
        $mapping['page']        = 'noimage';      // built-in
        $mapping['product']     = 'image';        // built-in
        $mapping['job_listing'] = 'jobposting';   // built-in
        $mapping['event']       = 'my-event';     // custom (see below)
        return $mapping;
    }
);

add_filter(
    'Municipio/TypesenseSearch/hitTemplates',
    function (array $templates): array {
        $templates[] = 'my-event';
        return $templates;
    }
);

add_filter(
    'Municipio/TypesenseSearch/hitTemplateView',
    function (string $view, string $key): string {
        if ($key === 'my-event') {
            // Point to a view inside your theme or another plugin
            return 'my-theme.search.hit-event';
        }
        return $view;
    },
    10,
    2
);

add_filter(
    'Municipio/TypesenseSearch/postTypeToTemplate',
    function (array $mapping): array {
        $mapping['event'] = 'my-event';
        return $mapping;
    }
);

// Re-index a post after your plugin changes data that should update the index.
// The post must already be published — nothing happens for drafts etc.
do_action('typesense_search/index_post', $post_id);

// Explicitly remove a post from the index.
do_action('typesense_search/deindex_post', $post_id);