Install the Bundle
Add to your composer.json:
composer require biblioverse/typesense-bundle
Register in config/bundles.php:
return [
// ...
Biblioverse\TypesenseBundle\TypesenseBundle::class => ['all' => true],
];
Configure Typesense Publish the default config:
php bin/console typesense:install
Update config/packages/typesense.yaml with your Typesense server details:
typesense:
client:
host: 'http://localhost:8108'
api_key: 'your-api-key'
connection_timeout_seconds: 2
First Use Case: Searching
Inject the TypesenseClient and perform a search:
use Biblioverse\TypesenseBundle\Client\TypesenseClientInterface;
class MyController
{
public function __construct(private TypesenseClientInterface $typesense)
{
}
public function search(Request $request)
{
$results = $this->typesense->search('books', $request->query->get('q'));
return $this->json($results);
}
}
TypesenseClientInterface (PSR-15 compliant).php bin/console typesense:install (config), typesense:schema:dump (schema export).TypesenseEntityListener for auto-indexing entities (requires config).Schema Management
Define schemas in YAML (e.g., config/typesense/schemas/books.yaml):
name: books
fields:
title: {type: string}
author: {type: string, optional: true}
Load dynamically via:
$this->typesense->loadSchema('books');
CRUD Operations Use fluent methods for collections:
// Create
$this->typesense->create('books', ['title' => 'Laravel Up & Running']);
// Update
$this->typesense->update('books', 123, ['author' => 'Matt Stauffer']);
// Delete
$this->typesense->delete('books', 123);
Search with Filters Combine queries and filters:
$results = $this->typesense->search('books', 'laravel', [
'filter_by' => 'author:Matt Stauffer',
'per_page' => 10,
]);
Bulk Operations
Use TypesenseBulkClient for batch imports:
$this->typesense->bulk()->add('books', [
['id' => 1, 'title' => 'Book 1'],
['id' => 2, 'title' => 'Book 2'],
])->commit();
$choices = $this->typesense->search('authors', $request->query->get('q'));
$form->add('author', ChoiceType::class, ['choices' => array_column($choices, 'name', 'id')]);
TypesenseClient to cache responses (e.g., with Symfony Cache component).Schema Mismatches
typesense:schema:rebuild to drop and recreate collections:
php bin/console typesense:schema:rebuild books
books_v1.yaml) and track changes.Rate Limiting
connection_timeout_seconds (2s) may trigger retries unnecessarily.Doctrine Auto-Indexing
TypesenseEntityListener may not handle circular references or complex types.@Typesense\Ignore or override the listener.API Key Exposure
typesense.yaml risks leaks.%env% or parameter_bag:
api_key: '%env(TYPESENSE_API_KEY)%'
config/packages/monolog.yaml:
handlers:
typesense:
type: stream
path: "%kernel.logs_dir%/%env(default::typesense).log%"
level: debug
TypesenseClient to log raw API calls:
$client->getHttpClient()->getEventDispatcher()->addListener(
'request',
function (RequestEvent $event) {
error_log($event->getRequest()->getBody()->getContents());
}
);
Custom Clients
Implement TypesenseClientInterface for specialized behavior (e.g., retry logic):
class RetryTypesenseClient implements TypesenseClientInterface
{
public function __construct(private TypesenseClientInterface $decorated) {}
public function search(string $collection, string $query, array $options = []): array
{
return $this->decorated->search($collection, $query, $options);
}
// Add retry logic here
}
Schema Builders
Extend SchemaBuilder to support dynamic fields:
class DynamicSchemaBuilder extends SchemaBuilder
{
public function addDynamicField(string $name): self
{
$this->schema['fields'][$name] = ['type' => 'string', 'optional' => true];
return $this;
}
}
Event Listeners
Subscribe to TypesenseEvents (e.g., SchemaLoadedEvent) for post-processing:
$dispatcher->addListener(TypesenseEvents::SCHEMA_LOADED, function (SchemaLoadedEvent $event) {
// Modify schema before indexing
});
collections API to group related schemas (e.g., books, reviews under library).tenant_123_books) and use filter_by for isolation.php bin/console typesense:health
How can I help you explore Laravel packages today?