Getting Started with Laravel Solr: A Practical Guide

Integrating Apache Solr with Laravel: A Complete Guide
Introduction
In modern web applications, implementing robust search functionality is crucial. While Laravel provides built-in search capabilities, complex applications often require more sophisticated solutions. This guide will walk you through integrating Apache Solr with Laravel using the Solarium package to create a powerful search system.
Why Choose Solr for Laravel?
Laravel’s Native Search vs Solr
Laravel’s built-in search capabilities work well for basic needs, but Solr offers several advantages:
- Advanced Search Features:
- Full-text search with complex queries
- Faceted search capabilities
- Geospatial search
- Suggestion/autocomplete functionality
- Performance Benefits:
- Highly scalable for millions of records
- Fast query response times
- Efficient index management
- Built-in caching
- Additional Features:
- Custom scoring and relevance
- Advanced text analysis
- Multiple language support
- Real-time indexing
Setting Up Your Environment
Prerequisites
- PHP 8.1 or higher
- Laravel 10.x
- Apache Solr 8.11.2
- Composer
Step 1: Installing Solr
# Download Solr
wget https://downloads.apache.org/lucene/solr/8.11.2/solr-8.11.2.tgz
tar xzf solr-8.11.2.tgz
cd solr-8.11.2
# Start Solr
bin/solr start
# Create a new core
bin/solr create -c articles
Step 2: Creating a New Laravel Project
composer create-project laravel/laravel laravel-solr-demo
cd laravel-solr-demo
Step 3: Installing Required Packages
composer require solarium/solarium
Implementation Guide
Step 1: Setting Up the Service Provider
Create a new service provider to manage the Solr client:
php artisan make:provider SolrServiceProvider
Add the following configuration:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Solarium\Client;
use Solarium\Core\Client\Adapter\Curl;
use Symfony\Component\EventDispatcher\EventDispatcher;
class SolrServiceProvider extends ServiceProvider {
public function register() {
$this->app->singleton(Client::class, function($app) {
$adapter = new Curl(); $eventDispatcher = new EventDispatcher();
return new Client($adapter, $eventDispatcher,
[
'endpoint' => [ 'localhost' =>
[
'host' => env('SOLR_HOST', '127.0.0.1'),
'port' => env('SOLR_PORT', '8983'),
'path' => env('SOLR_PATH', '/solr/'),
'core' => env('SOLR_CORE', 'articles')
]
]
]);
});
}
}
Step 2: Creating the Solr Service
This service will handle all Solr operations:
namespace App\Services;
use Solarium\Client;
use App\Models\Article;
class SolrService
{
protected $client;
public function __construct(Client $client)
{
$this->client = $client;
}
public function indexArticle(Article $article)
{
$update = $this->client->createUpdate();
$doc = $update->createDocument();
$doc->id = $article->id;
$doc->title = $article->title;
$doc->content = $article->content;
$doc->author = $article->author;
$doc->category = $article->category;
$doc->tags = $article->tags;
$doc->published_at = $article->published_at->format('Y-m-d\TH:i:s\Z');
$update->addDocument($doc);
$update->addCommit();
return $this->client->update($update);
}
public function search(string $query, array $filters = [], int $page = 1, int $perPage = 10)
{
$select = $this->client->createSelect();
// Set basic query
if (!empty($query)) {
$select->setQuery($query);
}
// Add filters
if (!empty($filters)) {
$filterQuery = $select->createFilterQuery('filters');
$filterQueries = [];
foreach ($filters as $field => $value) {
if (!empty($value)) {
$filterQueries[] = "$field:\"$value\"";
}
}
if (!empty($filterQueries)) {
$filterQuery->setQuery(implode(' AND ', $filterQueries));
}
}
// Add faceting
$facetSet = $select->getFacetSet();
$facetSet->createFacetField('category')->setField('category');
$facetSet->createFacetField('author')->setField('author');
// Set pagination
$select->setStart(($page - 1) * $perPage)->setRows($perPage);
// Execute query
$result = $this->client->select($select);
return [
'total' => $result->getNumFound(),
'documents' => array_map(function($doc) {
return [
'id' => $doc->id,
'title' => $doc->title,
'content' => $doc->content,
'author' => $doc->author,
'category' => $doc->category,
'published_at' => $doc->published_at
];
}, $result->getDocuments()),
'facets' => [
'category' => $result->getFacetSet()->getFacet('category')->getValues(),
'author' => $result->getFacetSet()->getFacet('author')->getValues(),
]
];
}
public function suggest(string $query)
{
$suggester = $this->client->createSuggester();
$suggester->setDictionary('suggest');
$suggester->setQuery($query);
$suggester->setCount(5);
$result = $this->client->suggester($suggester);
return $result->getAllSuggestions();
}
public function deleteFromIndex($id)
{
$update = $this->client->createUpdate();
$update->addDeleteById($id);
$update->addCommit();
return $this->client->update($update);
}
}
Step 3: Setting Up the Model
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
protected $fillable = [
'title',
'content',
'author',
'category',
'tags',
'published_at'
];
protected $casts = [
'published_at' => 'datetime',
'tags' => 'array'
];
}
Step 4: Creating the Search Controller
namespace App\Http\Controllers;
use App\Services\SolrService;
use Illuminate\Http\Request;
class SearchController extends Controller
{
protected $solrService;
public function __construct(SolrService $solrService)
{
$this->solrService = $solrService;
}
public function search(Request $request)
{
$validated = $request->validate([
'q' => 'nullable|string',
'category' => 'nullable|string',
'author' => 'nullable|string',
'page' => 'nullable|integer|min:1',
]);
$results = $this->solrService->search(
$validated['q'] ?? '*:*',
array_filter([
'category' => $validated['category'] ?? null,
'author' => $validated['author'] ?? null,
]),
$validated['page'] ?? 1
);
return response()->json($results);
}
public function suggest(Request $request)
{
$validated = $request->validate([
'q' => 'required|string|min:2'
]);
$suggestions = $this->solrService->suggest($validated['q']);
return response()->json($suggestions);
}
}
Key Features Implementation
1. Basic Search
public function search(string $query)
{
$select = $this->client->createSelect();
$select->setQuery($query);
return $this->client->select($select);
}
2. Faceted Search
$facetSet = $select->getFacetSet();
$facetSet->createFacetField('category')->setField('category');
$facetSet->createFacetField('author')->setField('author');
3. Filtered Search
$filterQuery = $select->createFilterQuery('filters');
$filterQuery->setQuery("category:\"$category\"");
Configuration
Environment Setup
Add these variables to your .env
file:
SOLR_HOST=127.0.0.1
SOLR_PORT=8983
SOLR_PATH=/solr/
SOLR_CORE=articles
Register Service Provider
Add to config/app.php
:
'providers' => [
App\Providers\SolrServiceProvider::class,
],
Best Practices
- Index Management
- Implement batch indexing for large datasets
- Use delta updates for real-time changes
- Regularly optimize your indexes
- Performance Optimization
- Cache common queries
- Use field type optimization
- Implement proper error handling
- Search Experience
- Implement proper highlighting
- Add spelling suggestions
- Use boost queries for better relevance
Common Issues and Solutions
- Connection Issues
- Verify Solr is running
- Check firewall settings
- Validate core name and path
- Performance Issues
- Optimize index settings
- Use proper field types
- Implement caching
- Indexing Problems
- Verify field mappings
- Check data types
- Monitor index size
Testing the Implementation
Unit Tests
public function test_basic_search()
{
$result = $this->solrService->search('test query');
$this->assertNotNull($result);
}
Feature Tests
public function test_search_endpoint()
{
$response = $this->get('/api/search?q=test');
$response->assertStatus(200);
}
Conclusion
Integrating Solr with Laravel using Solarium provides a powerful search solution for complex applications. While the setup requires more effort than using Laravel’s native search, the benefits in terms of performance, scalability, and features make it worthwhile for larger applications.