nelmio/solarium-bundle
NelmioSolariumBundle integrates the Solarium PHP client with Symfony, providing a preconfigured Solr client as a service, simple configuration for your Solr endpoints, and convenient querying of Apache Solr from any Symfony application.
This is a Symfony bundle that wires the solarium/solarium Solr client into a Symfony container. It is not a Laravel package: there is no service provider, no config/solr.php, and no package discovery. If you are working in Laravel, the realistic options are:
solarium/solarium directly (recommended for Laravel-only projects) and write a small service provider that mirrors this bundle's config shape.Minimal Symfony setup (bundle ^6.1, which supports both Solarium 6 and 7):
composer require nelmio/solarium-bundle:^6.1 solarium/solarium:^7.0
// config/bundles.php
Nelmio\SolariumBundle\NelmioSolariumBundle::class => ['all' => true],
# config/packages/nelmio_solarium.yaml
nelmio_solarium:
endpoints:
default:
scheme: http
host: '%env(SOLR_HOST)%'
port: 8983
path: /solr
core: movies
clients:
default:
endpoints: [default]
adapter_timeout: 5
Then inject the client and run your first query:
use Solarium\Core\Client\ClientInterface;
final class MovieSearch
{
public function __construct(private ClientInterface $client) {}
public function search(string $q): array
{
$select = $this->client->createSelect();
$select->setQuery($q === '' ? '*:*' : $q);
$select->setRows(20);
return $this->client->select($select)->getData()['response']['docs'] ?? [];
}
}
Where to look first:
solarium/solarium UPGRADE notes for the major you install (6 vs 7); this bundle contributes configuration and DI, not query abstraction./solr/#/movies/schema) to confirm field names before writing any query code.First useful use case: a search endpoint over one core, backed by a small console command or job that indexes records. Get read and write paths working against a real Solr container before adding facets or highlighting.
Configuration-first client wiring. Define endpoints and named clients in config, then inject the default client or a named one. Keep host/port/core in env vars; do not hardcode cores in query classes. For multiple cores, define multiple clients and inject by service ID rather than switching cores at runtime.
Type-hint the interface, not Solarium\Client. With 6.1 spanning Solarium 6 and 7, depend on Solarium\Core\Client\ClientInterface and the query/result interfaces (QueryInterface, SelectQueryInterface). That keeps your application code compiling across the Solarium major boundary and lets you swap in a decorator or fake in tests.
Mirror the config shape in Laravel. If you are not using Symfony, recreate the same two-level structure: an endpoints array and a clients map, with one default. Bind ClientInterface as a singleton in a service provider and resolve it via constructor injection. This keeps the mental model identical to the bundle and makes a later migration to Symfony trivial.
Indexing as queued jobs. The common day-to-day workflow is: Eloquent model saved → dispatch IndexDocument job → build an UpdateQuery with add/delete → execute. Batch with the buffered-add plugin for bulk imports rather than one update per row. Keep indexing idempotent so retries are safe.
$update = $client->createUpdate();
$doc = $update->createDocument();
$doc->id = (string) $movie->id;
$doc->title = $movie->title;
$doc->setField('genre', $movie->genres->pluck('name')->all());
$update->addDocument($doc);
$update->addCommitWithin(5000); // avoid hard commits in the request path
$client->update($update);
Deletes before re-index. For updates where fields can disappear, delete by ID then add, or use atomic updates deliberately. Solr merges documents, so stale fields survive plain add calls.
Query objects per use case. Build a small MovieQuery class per screen or endpoint that returns a select query object, then a single executor that applies pagination, facets, and hydration. This keeps controllers thin and makes it easy to add highlighting or spellcheck later.
Hydration. Solarium returns arrays, not models. Map $result->getData()['response']['docs'] into DTOs or re-fetch Eloquent models by ID for detail views. Do not hydrate full models from Solr alone unless you accept eventual consistency.
Testing. Do not mock Solarium's internals. Either run a real Solr via Docker in CI, or wrap the client behind your own SearchClient interface and fake that. This also gives you a seam to swap in a different engine later.
Health and observability. Add a ping check to your health endpoint and log query time plus responseHeader.QTime. Solr failures are usually timeouts or config drift, and both are easier to diagnose with the raw query string logged.
solarium/solarium ^6.0 || ^7.0. Pin the bundle and Solarium together in composer.json, and read the README section matching your installed tag — config keys like endpoints (map vs. DSN list) and adapter options differ across majors. Note the bundle's own constraint did not change its config schema; only the supported Solarium range moved.composer require step, and 7 drops APIs deprecated through the 6.x line — grep for deprecated constructors, adapter wiring, and removed helper methods before you flip the constraint. If you want a no-code-change release, take 6.1.0 and stay on Solarium 6.addCommit() in a request path will wreck latency on a busy index. Prefer commitWithin, auto-commit, or a scheduled commit. Hard commits are expensive; soft commits trade durability for speed.deleteByQuery on a large index can be slow and is not transactional. For reindexing, consider a second core plus an alias swap instead of deleting in place.responseHeader.status and error bodies; Solarium throws on HTTP errors but partial failures can be subtle. Validate field names against the live schema in tests.adapter_timeout and connection timeout for both reads and indexing. If Solr sits behind auth or a proxy, configure that at the endpoint/adapter level rather than patching requests. The default curl adapter bypasses Laravel's HTTP client, so Http::fake() will not intercept anything.start/rows and numFound. Build pagination yourself and cap deep paging; Solr's start costs grow with offset.*_s, *_i, *_t suffixes are schema conventions. Getting them wrong yields either type errors or fields that never match a query.Solarium\Core\Plugin\AbstractPlugin) for logging or request decoration, custom adapters, and Solarium's event dispatcher for pre/post request hooks. This is the clean way to add tracing without touching call sites — and plugin/event signatures are exactly the kind of thing that shifts between Solarium majors, so check them when you cross to 7./solr path — some managed providers require a different base path and a core name embedded in the URL.How can I help you explore Laravel packages today?