cmsig/seal
SEAL Core (cmsig/seal) is a Search Engine Abstraction Layer inspired by Doctrine DBAL and Flysystem. Provides a unified API and schema for indexing, searching, and filtering across multiple search engines. Part of the cmsig/search project.
Install the Package
composer require cmsig/seal
Ensure your project uses PHP 8.1+ (check composer.json constraints).
Configure a Search Engine
Define a supported adapter (e.g., Elasticsearch, OpenSearch, or Meilisearch) in config/seal.php:
'engines' => [
'default' => [
'driver' => 'elasticsearch',
'hosts' => ['http://localhost:9200'],
'index' => 'your_index_name',
],
],
First Use Case: Basic Search
Inject the SearchEngine facade or bind it in a service provider:
use CMSIG\Search\SearchEngine;
$results = SearchEngine::search('default', 'query');
Documentation Start with the official docs for schema definitions, indexing, and advanced queries.
Define your index schema in a Laravel migration or service class:
use CMSIG\Search\Schema\Builder;
$schema = Builder::create()
->addTextField('title')
->addKeywordField('category')
->addIntegerField('views')
->build();
SearchEngine::createIndex('default', $schema);
Use the Indexer to push data to the search engine:
$indexer = SearchEngine::indexer('default');
$indexer->index([
'id' => 1,
'title' => 'Laravel SEAL Guide',
'category' => 'tutorial',
'views' => 1000,
]);
Leverage the query builder for complex searches:
$results = SearchEngine::search('default')
->query('laravel')
->filter('category', 'tutorial')
->sortBy('views', 'desc')
->limit(10)
->get();
Integrate with Laravel’s pagination:
$results = SearchEngine::search('default')
->query('guide')
->paginate(15); // Returns a LengthAwarePaginator
Use batch indexing for performance:
$indexer = SearchEngine::indexer('default');
$indexer->bulkIndex([
['id' => 1, 'title' => 'Post 1'],
['id' => 2, 'title' => 'Post 2'],
]);
Bind the SearchEngine to the container for dependency injection:
public function register()
{
$this->app->bind('search', function ($app) {
return new SearchEngine(config('seal.engines.default'));
});
}
Extend Eloquent models with searchable traits or repositories:
use CMSIG\Search\Eloquent\Searchable;
class Post extends Model
{
use Searchable;
protected $searchable = ['title', 'content'];
}
Transform search results into API responses:
public function toArray($request)
{
return [
'data' => $this->searchResults->map(fn ($item) => [
'id' => $item['id'],
'title' => $item['title'],
]),
];
}
Schema Mismatches
SearchEngine::updateIndex() or recreate the index with a new schema.index_v2) and migrate data incrementally.Connection Timeouts
$indexer->bulkIndex($posts->chunk(100));
Case Sensitivity
->addKeywordField('category')->caseInsensitive()
Pagination Conflicts
paginate() may conflict with search engine pagination.->get() for raw results or manually paginate:
$results = SearchEngine::search('default')->query('test')->limit(10)->offset(20)->get();
Missing Dependencies
elasticsearch) require additional packages (e.g., elasticsearch/elasticsearch).composer require elasticsearch/elasticsearch
Enable Logging Configure the search engine to log queries:
'engines' => [
'default' => [
'driver' => 'elasticsearch',
'logger' => \Monolog\Logger::create('seal'),
],
],
Raw Query Inspection Access the underlying query for debugging:
$query = SearchEngine::search('default')->query('test')->getQuery();
dd($query->toArray());
Adapter-Specific Tools
curl or Kibana to inspect indices.meilisearch CLI or dashboard.Custom Adapters
Extend CMSIG\Search\Adapter\AbstractAdapter to support new search engines:
class CustomAdapter extends AbstractAdapter
{
public function search($query, array $options = [])
{
// Implement custom logic
}
}
Query Builders
Extend CMSIG\Search\Query\Builder for domain-specific queries:
class PostQueryBuilder extends Builder
{
public function byAuthor($author)
{
return $this->filter('author', $author);
}
}
Schema Extensions
Add custom field types by extending CMSIG\Search\Schema\Field:
class CustomField extends Field
{
public function getType(): string
{
return 'custom';
}
}
Event Listeners
Listen to search events (e.g., IndexCreated, DocumentIndexed) for analytics or notifications:
SearchEngine::listen('document.indexed', function ($event) {
Log::info("Indexed document: {$event->document['id']}");
});
Default Engine
Always specify a default engine in config/seal.php to avoid runtime errors.
Index Naming Use lowercase, snake_case for index names to avoid issues with some search engines.
Field Mapping Explicitly map Eloquent fields to search fields to avoid ambiguity:
protected $searchable = [
'title' => 'search_title',
'body' => 'search_content',
];
Environment-Specific Config Use Laravel’s config caching or environment variables for dynamic engine settings:
'engines' => [
'default' => [
'hosts' => env('SEARCH_HOSTS', ['http://localhost:9200']),
],
],
How can I help you explore Laravel packages today?