laravel-searchable maintained by eslam-dev
Laravel Searchable
A Laravel package for dedicated searchable text columns on Eloquent models: automatic fill on save, text cleaning, and MySQL FULLTEXT search.
Features
✅ Migration Macros - Simple searchableColumn() macro for creating LONGTEXT columns with FULLTEXT indexes
✅ Automatic Population - Searchable columns are automatically updated on model save
✅ Text Cleaning - Comprehensive text normalization (HTML stripping, Arabic normalization, diacritic removal)
✅ FULLTEXT Search - Native MySQL FULLTEXT search with natural language mode
✅ Multiple Columns - Support for multiple searchable columns per model
✅ Relationship Support - Extract text from relationships using dot notation
✅ Translatable Fields - Automatic extraction from JSON/translatable fields
✅ Queue Support - Rebuild searchable columns via queue for large datasets
✅ Highly Configurable - Customize cleaning rules, separators, and extraction methods
Requirements
- PHP 8.2+
- Laravel 12.0+
- MySQL 5.6+ or MariaDB 10.0+
Installation
Install via Composer:
composer require eslam-dev/laravel-searchable
Publish the configuration file:
php artisan vendor:publish --tag=laravel-searchable-config
Quick Start
1. Add Searchable Columns to Migration
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->json('title');
$table->json('description');
$table->string('sku');
// Add searchable columns with automatic FULLTEXT index
$table->searchableColumn('title_search');
$table->searchableColumn('description_search');
$table->searchableColumn('combined_search');
$table->timestamps();
});
2. Add Trait to Model
use Illuminate\Database\Eloquent\Model;
use EslamDev\SearchableColumns\Traits\HasSearchableColumns;
class Product extends Model
{
use HasSearchableColumns;
protected array $searchableColumns = [
// Simple: one source field
'title_search' => ['title'],
// Multiple source fields
'description_search' => ['description', 'short_description'],
// Combined search across multiple fields
'combined_search' => ['title', 'description', 'sku'],
];
}
3. Search Your Data
// Search across all defined searchable columns
$products = Product::search('iphone 13 pro')->get();
// Search specific column
$products = Product::searchIn('title_search', 'iphone')->get();
// Search multiple columns
$products = Product::searchIn(['title_search', 'description_search'], 'phone')->get();
// Combine with other filters
$products = Product::search('laptop')
->where('status', 'published')
->inStock()
->get();
Advanced Usage
Relationship Fields
Extract text from related models:
protected array $searchableColumns = [
'search_text' => [
'title',
'description',
'brand.name', // BelongsTo relationship
'categories.*.name', // BelongsToMany/HasMany
],
];
Translatable Fields
For models using Spatie's Translatable or JSON fields:
protected array $searchableColumns = [
'search_text' => [
'sources' => ['title', 'description', 'brand.name'],
'extract_translations' => 'all', // 'all', 'default', or false
'separator' => ' | ',
],
];
Options:
'all'- Combines all translations: "Phone هاتف"'default'- Uses only the default localefalse- Treats as regular string
Custom Cleaning Rules
Override cleaning rules per model:
protected array $searchableCleaningRules = [
'strip_html' => true,
'decode_entities' => true,
'normalize_arabic' => true,
'remove_diacritics' => true,
'lowercase' => true,
'remove_symbols' => false, // Keep symbols
'collapse_whitespace' => true,
];
LIKE Fallback
For cases where FULLTEXT isn't suitable:
// Force LIKE search
$products = Product::searchLike('title_search', 'partial')->get();
// Search multiple columns with LIKE
$products = Product::searchLike(['title_search', 'description_search'], 'text')->get();
Artisan Commands
Rebuild Searchable Columns
Rebuild searchable columns for existing records:
# Rebuild all models
php artisan searchable:rebuild
# Specific model
php artisan searchable:rebuild --model="App\Models\Product"
# With progress bar
php artisan searchable:rebuild --model="App\Models\Product" --verbose
# Queue processing for large datasets
php artisan searchable:rebuild --model="App\Models\Product" --queue
# Specific queue
php artisan searchable:rebuild --model="App\Models\Product" --queue --queue-name=search-rebuild
Configuration
The configuration file config/laravel-searchable.php provides extensive customization options:
return [
// Enable/disable FULLTEXT indexes
'enable_fulltext' => true,
// Default text cleaning rules
'cleaning_rules' => [
'strip_html' => true,
'decode_entities' => true,
'normalize_arabic' => true,
'remove_diacritics' => true,
'lowercase' => true,
'remove_symbols' => true,
'collapse_whitespace' => true,
],
// Arabic normalization mappings
'arabic_normalizations' => [
'أ' => 'ا',
'إ' => 'ا',
'آ' => 'ا',
'ة' => 'ه',
'ى' => 'ي',
],
// Translation extraction mode
'extract_translations' => 'all', // 'all', 'default', or false
// Queue settings
'queue' => 'default',
'chunk_size' => 500,
// ... and more
];
Text Cleaning Pipeline
The package applies a comprehensive cleaning pipeline:
- Strip HTML - Removes all HTML tags
- Decode Entities - Converts HTML entities (
&→&) - Normalize Arabic - Standardizes Arabic character variants
- Remove Diacritics - Strips Arabic tashkeel marks
- Convert to Lowercase - Standardizes case for better matching
- Remove Symbols - Strips punctuation and special characters
- Collapse Whitespace - Normalizes spacing
Performance Optimization
MySQL Configuration
For better Arabic search, configure MySQL:
# /etc/mysql/my.cnf
[mysqld]
ft_min_word_length = 3
Then rebuild the FULLTEXT index:
OPTIMIZE TABLE products;
Queue Processing
For large datasets, use queue processing:
php artisan searchable:rebuild --model="App\Models\Product" --queue
Configure chunk size in config/laravel-searchable.php:
'chunk_size' => 500, // Adjust based on your needs
Eager Loading
The package automatically eager loads relationships used in searchable columns to prevent N+1 queries.
Testing
The package includes comprehensive unit and feature tests:
composer test
Test Requirements
Tests require a MySQL database. Configure in phpunit.xml:
<env name="DB_CONNECTION" value="mysql"/>
<env name="DB_DATABASE" value="searchable_test"/>
<env name="DB_USERNAME" value="root"/>
<env name="DB_PASSWORD" value=""/>
Examples
E-commerce Product Search
class Product extends Model
{
use HasSearchableColumns, HasTranslations;
public $translatable = ['title', 'description'];
protected array $searchableColumns = [
'search_text' => [
'title',
'description',
'sku',
'barcode',
'brand.name',
'categories.*.name',
],
];
}
// Search usage
$results = Product::search('iphone 13 pro')
->published()
->inStock()
->get();
Multi-language Content
class Article extends Model
{
use HasSearchableColumns, HasTranslations;
public $translatable = ['title', 'content'];
protected array $searchableColumns = [
'search_text' => [
'sources' => ['title', 'content', 'author.name'],
'extract_translations' => 'all', // Index all languages
],
];
}
// Searches in both English and Arabic
$results = Article::search('tutorial')->get();
Selective Searchable Columns
class Post extends Model
{
use HasSearchableColumns;
protected array $searchableColumns = [
'title_search' => ['title'],
'content_search' => ['content', 'excerpt'],
'full_search' => ['title', 'content', 'excerpt', 'tags.*.name'],
];
}
// Search only titles
Post::searchIn('title_search', 'laravel')->get();
// Search content
Post::searchIn('content_search', 'tutorial')->get();
// Full search
Post::search('laravel tutorial')->get();
Troubleshooting
FULLTEXT Search Not Working
- Ensure you're using MySQL 5.6+ or MariaDB 10.0+
- Check that FULLTEXT indexes are created:
SHOW INDEX FROM products WHERE Key_name LIKE '%search%'; - Verify minimum word length matches your content:
SHOW VARIABLES LIKE 'ft_min_word_length';
Arabic Search Issues
- Set
ft_min_word_length = 3in MySQL configuration - Rebuild FULLTEXT indexes:
OPTIMIZE TABLE products; - Verify Arabic normalization is enabled in config
Performance Issues
- Use queue processing for bulk rebuilds
- Adjust chunk size in configuration
- Add database indexes on frequently filtered columns
- Consider using Laravel Scout for more advanced search needs
Security
If you discover any security-related issues, please open a private security advisory on the repository instead of using the public issue tracker.
License
The MIT License (MIT). Please see License File for more information.
Credits
- eslam-dev
- All Contributors
Support
- 🐛 Issues: GitHub Issues
- 📖 Documentation: Full Documentation