PHP code example of monkeyscloud / monkeyslegion-search
1. Go to this page and download the library: Download monkeyscloud/monkeyslegion-search library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
monkeyscloud / monkeyslegion-search example snippets
declare(strict_types=1);
namespace App\Entity;
use MonkeysLegion\Entity\Attributes\{Entity, Field, Id, ObservedBy};
use MonkeysLegion\Search\Attributes\{Searchable, SearchField, SearchIndex};
use MonkeysLegion\Search\Observers\SearchObserver;
use MonkeysLegion\Search\Traits\SearchableTrait;
#[Entity(table: 'products')]
#[Searchable(index: 'products', engine: 'default', autoSync: true)]
#[ObservedBy(SearchObserver::class)]
#[SearchIndex(
stopWords: ['the', 'a', 'an'],
synonyms: ['phone' => ['mobile', 'cell', 'smartphone']],
)]
class Product
{
use SearchableTrait;
#[Id]
#[Field(type: 'unsignedBigInt', autoIncrement: true)]
public private(set) int $id;
#[Field(type: 'string', length: 255)]
#[SearchField(searchable: true, filterable: true, sortable: true, weight: 3)]
public string $name;
#[Field(type: 'text')]
#[SearchField(searchable: true, weight: 1)]
public string $description;
#[Field(type: 'decimal', precision: 10, scale: 2)]
#[SearchField(filterable: true, sortable: true)]
public string $price;
#[Field(type: 'string', length: 100)]
#[SearchField(filterable: true, facetable: true)]
public string $category;
#[Field(type: 'boolean')]
#[SearchField(filterable: true)]
public bool $in_stock;
#[Field(type: 'string', length: 20)]
public string $status = 'published';
// Control exactly what gets indexed
public function toSearchableArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'price' => (float) $this->price,
'category' => $this->category,
'in_stock' => $this->in_stock,
];
}
// Only index published products
public function shouldBeSearchable(): bool
{
return $this->status === 'published';
}
}
use MonkeysLegion\Search\SearchManager;
final class ProductController
{
public function __construct(
private readonly SearchManager $search,
) {}
public function index(ServerRequestInterface $request): Response
{
$q = $request->getQueryParams()['q'] ?? '';
$results = $this->search->index('products')
->query($q)
->where('in_stock', '=', true)
->whereBetween('price', 10.0, 500.0)
->facet('category', 'brand')
->sortBy('price', SortDirection::Asc)
->highlight('name', 'description')
->page(1, perPage: 20)
->get();
// $results->total — Total matching documents
// $results->hits — list<SearchHit> on this page
// $results->facets — list<Facet> with value counts
// $results->lastPage — Total pages (property hook)
// $results->hasMore — More pages? (property hook)
// $results->took — Query time in ms
return new Response(json: [
'data' => array_map(fn($h) => $h->document, $results->hits),
'total' => $results->total,
'facets' => $results->facets,
]);
}
}
// Static search — returns a Builder for the entity's index
$results = Product::search('wireless headphones')
->where('category', '=', 'electronics')
->page(1)
->get();
// Index a single entity
$product->searchable();
// Remove from index
$product->unsearchable();
// Bulk index multiple entities
Product::makeSearchable($products);
// Bulk remove
Product::makeUnsearchable($products);
#[Entity(table: 'products')]
#[Searchable(autoSync: true)]
#[ObservedBy(SearchObserver::class)]
class Product { use SearchableTrait; }
// Register once at boot
LifecycleDispatcher::registerSubscriber(SearchSubscriber::class);
// Enable per-entity
#[Searchable(autoSync: true, queue: true)]
class Product { ... }
// Or dispatch jobs manually
use MonkeysLegion\Search\Jobs\{IndexDocumentJob, DeleteDocumentJob, BulkIndexJob};
$dispatcher->dispatch(new IndexDocumentJob('products', '42', $document));
$dispatcher->dispatch(new DeleteDocumentJob('products', '42'));
$dispatcher->dispatch(new BulkIndexJob('products', $documents));
$results = $search->index('restaurants')
->query('pizza')
->near(latitude: 40.7128, longitude: -74.0060, geoField: 'location')
->withinRadius(distanceKm: 5.0)
->sortByDistance(latitude: 40.7128, longitude: -74.0060)
->get();
// Each hit
// Process millions of documents without loading all into memory
foreach ($search->index('logs')->query('error')->cursor(chunkSize: 100) as $hit) {
processLogEntry($hit->document);
}
use MonkeysLegion\Search\Index\Reindexer;
$reindexer = new Reindexer($search->engine());
// Basic reindex
$total = $reindexer->reindex(
indexName: 'products',
dataProvider: fn(int $offset, int $limit) => $repo->findChunk($offset, $limit),
chunkSize: 500,
onProgress: fn(int $indexed, int $total) => $output->writeln("{$indexed}/{$total}"),
totalCount: $repo->count(),
);
// Zero-downtime reindex (OpenSearch/Elasticsearch)
$reindexer->reindex(
indexName: 'products',
dataProvider: $provider,
useAlias: true, // Creates products_v{timestamp} → atomic alias swap
);
// From entity class
$reindexer->reindexEntity(
entityClass: Product::class,
entityLoader: fn(int $offset, int $limit) => $repo->findAll($offset, $limit),
chunkSize: 500,
);