Search Implementation
InspireCMS provides flexible search capabilities to help users find content quickly and efficiently. This guide covers how to implement, customize, and optimize search functionality in your InspireCMS application.
Overview
The search system in InspireCMS allows you to:
- Search across all content types
- Filter results by document type, status, and other criteria
- Implement full-text search
- Customize search relevance and ranking
- Create advanced search interfaces
Basic Search Implementation
Using the Built-in Search
InspireCMS includes a basic search function that can be used out of the box:
// Simple search across all content
$results = inspirecms_content()->search('keyword');
// Search with specific document type
$results = inspirecms_content()
->documentType('blog_post')
->search('keyword');
// Search with pagination
$results = inspirecms_content()
->search('keyword')
->paginate(10);
Search Controller Example
Here's a simple search controller implementation:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SearchController extends Controller
{
public function index(Request $request)
{
$query = $request->input('q');
$documentType = $request->input('type');
$page = $request->input('page', 1);
if (empty($query)) {
return view('search.index', [
'query' => '',
'results' => null,
]);
}
$searchBuilder = inspirecms_content()
->isPublished(true);
if ($documentType) {
$searchBuilder->documentType($documentType);
}
$results = $searchBuilder
->search($query)
->paginate(12);
return view('search.index', [
'query' => $query,
'results' => $results,
]);
}
}
Search Template Example
Create a search results template:
resources/views/search/index.blade.php
<x-inspirecms-your-theme::layout>
<div class="container">
<h1>Search Results</h1>
<form action="{{ route('search') }}" method="GET" class="search-form">
<div class="input-group mb-4">
<input
type="text"
name="q"
value="{{ $query }}"
class="form-control"
placeholder="Search..."
required
>
<select name="type" class="form-select">
<option value="">All Content</option>
<option value="page" {{ request('type') == 'page' ? 'selected' : '' }}>Pages</option>
<option value="blog_post" {{ request('type') == 'blog_post' ? 'selected' : '' }}>Blog Posts</option>
<option value="product" {{ request('type') == 'product' ? 'selected' : '' }}>Products</option>
</select>
<button type="submit" class="btn btn-primary">Search</button>
</div>
</form>
@if($query)
<div class="search-results">
<p>{{ $results->total() }} results found for "{{ $query }}"</p>
@if($results->count() > 0)
<div class="results-list">
@foreach($results as $item)
<div class="result-item">
<h3>
<a href="{{ $item->getUrl() }}">
{{ $item->getTitle() }}
</a>
</h3>
@if($item->getPropertyValue('content', 'summary'))
<p>{{ Str::limit(strip_tags($item->getPropertyValue('content', 'summary')), 150) }}</p>
@endif
<div class="meta">
<span class="document-type">{{ $item->document_type }}</span>
<span class="publication-date">{{ $item->published_at?->diffForHumans() }}</span>
</div>
</div>
@endforeach
</div>
<div class="pagination">
{{ $results->appends(request()->except('page'))->links() }}
</div>
@else
<div class="no-results">
<p>No results found. Please try different keywords.</p>
</div>
@endif
</div>
@endif
</div>
</x-inspirecms-your-theme::layout>
Registering the Search Route
Register the search route in your routes/web.php file:
Route::get('/search', [\App\Http\Controllers\SearchController::class, 'index'])->name('search');
Advanced Search Configuration
Search Configuration Options
You can configure search behavior in your config/inspirecms.php file:
config/inspirecms.php
'search' => [
'driver' => env('SEARCH_DRIVER', 'database'), // Options: database, elastic, algolia, custom
'index_prefix' => env('SEARCH_INDEX_PREFIX', 'inspirecms_'),
'min_search_length' => 2,
'highlight' => [
'enabled' => true,
'tag' => 'mark',
],
'weights' => [
'title' => 10,
'content' => 5,
'tags' => 3,
'path' => 2,
],
'include_fields' => [
'title',
'properties.content.body',
'properties.seo.meta_description',
'properties.categorization.tags',
],
],
Custom Search Provider
You can create a custom search provider to implement specialized search functionality:
namespace App\Search;
use SolutionForest\InspireCms\Search\SearchProviderInterface;
use SolutionForest\InspireCms\Models\Content;
use Illuminate\Pagination\LengthAwarePaginator;
class CustomSearchProvider implements SearchProviderInterface
{
public function search(string $query, array $options = []): LengthAwarePaginator
{
// Custom search logic
// ...
// Return paginated results
return new LengthAwarePaginator(
$items,
$totalCount,
$options['per_page'] ?? 10,
$options['page'] ?? 1,
[
'path' => request()->url(),
'query' => request()->query(),
]
);
}
public function indexContent(Content $content): void
{
// Logic to index content
// ...
}
public function removeFromIndex(Content $content): void
{
// Logic to remove content from index
// ...
}
public function updateIndex(Content $content): void
{
// Logic to update content in index
// ...
}
}
Register your custom search provider:
config/inspirecms.php
'search' => [
'driver' => 'custom',
'providers' => [
'custom' => \App\Search\CustomSearchProvider::class,
],
// Other search settings...
],
Integrating Elasticsearch
InspireCMS can integrate with Elasticsearch for more powerful search capabilities:
Installation and Configuration
- Install the required packages:
composer require elasticsearch/elasticsearch
- Configure Elasticsearch connection:
config/inspirecms.php
'search' => [
'driver' => 'elastic',
'connections' => [
'elastic' => [
'hosts' => [
env('ELASTICSEARCH_HOST', 'localhost:9200'),
],
'username' => env('ELASTICSEARCH_USERNAME'),
'password' => env('ELASTICSEARCH_PASSWORD'),
],
],
'index_prefix' => env('ELASTICSEARCH_INDEX_PREFIX', 'inspirecms_'),
'index_settings' => [
'number_of_shards' => 1,
'number_of_replicas' => 0,
],
],
- Create an Elasticsearch mapping:
namespace App\Console\Commands;
use Elasticsearch\Client;
use Illuminate\Console\Command;
class CreateElasticsearchMapping extends Command
{
protected $signature = 'elasticsearch:create-mapping';
protected $description = 'Create Elasticsearch mapping for InspireCMS content';
public function handle(Client $elasticsearch)
{
$index = config('inspirecms.search.index_prefix') . 'content';
// Delete index if it exists
if ($elasticsearch->indices()->exists(['index' => $index])) {
$elasticsearch->indices()->delete(['index' => $index]);
}
// Create index with mapping
$elasticsearch->indices()->create([
'index' => $index,
'body' => [
'settings' => config('inspirecms.search.index_settings'),
'mappings' => [
'properties' => [
'id' => ['type' => 'keyword'],
'title' => [
'type' => 'text',
'analyzer' => 'standard',
'fields' => [
'keyword' => ['type' => 'keyword'],
],
],
'path' => ['type' => 'keyword'],
'document_type' => ['type' => 'keyword'],
'content' => ['type' => 'text'],
'tags' => ['type' => 'keyword'],
'published_at' => ['type' => 'date'],
// Additional fields...
],
],
],
]);
$this->info("Index {$index} created successfully.");
}
}
- Index content to Elasticsearch:
namespace App\Console\Commands;
use Elasticsearch\Client;
use Illuminate\Console\Command;
use SolutionForest\InspireCms\Models\Content;
class IndexContentToElasticsearch extends Command
{
protected $signature = 'elasticsearch:index-content {--fresh} {--id=}';
protected $description = 'Index InspireCMS content to Elasticsearch';
public function handle(Client $elasticsearch)
{
$index = config('inspirecms.search.index_prefix') . 'content';
if ($this->option('fresh')) {
$this->call('elasticsearch:create-mapping');
}
$query = Content::query()->isPublished();
if ($id = $this->option('id')) {
$query->where('id', $id);
$this->info("Indexing content with ID: {$id}");
} else {
$this->info("Indexing all published content");
}
$count = 0;
$query->chunk(100, function ($contents) use ($elasticsearch, $index, &$count) {
foreach ($contents as $content) {
$this->indexContent($elasticsearch, $index, $content);
$count++;
}
});
$this->info("Indexed {$count} content items.");
}
protected function indexContent(Client $elasticsearch, string $index, Content $content)
{
// Extract searchable text from content properties
$searchableText = $this->extractSearchableText($content);
$elasticsearch->index([
'index' => $index,
'id' => $content->id,
'body' => [
'id' => $content->id,
'title' => $content->getTitle(),
'path' => $content->getPath(),
'document_type' => $content->document_type,
'content' => $searchableText,
'tags' => $this->extractTags($content),
'published_at' => $content->published_at?->format('c'),
],
]);
}
protected function extractSearchableText(Content $content)
{
$text = $content->getTitle() . ' ';
// Add body content
if ($content->hasProperty('content', 'body')) {
$text .= strip_tags($content->getPropertyValue('content', 'body')) . ' ';
}
// Add summary
if ($content->hasProperty('content', 'summary')) {
$text .= strip_tags($content->getPropertyValue('content', 'summary')) . ' ';
}
// Add meta description
if ($content->hasProperty('seo', 'meta_description')) {
$text .= $content->getPropertyValue('seo', 'meta_description') . ' ';
}
return $text;
}
protected function extractTags(Content $content)
{
$tags = [];
// Extract tags from content
if ($content->hasProperty('categorization', 'tags')) {
$tags = array_merge($tags, (array) $content->getPropertyValue('categorization', 'tags'));
}
return $tags;
}
}