PHP code example of glueful / meilisearch

1. Go to this page and download the library: Download glueful/meilisearch 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/ */

    

glueful / meilisearch example snippets




return [
    'host' => env('MEILISEARCH_HOST', 'http://127.0.0.1:7700'),
    'key' => env('MEILISEARCH_KEY', null),
    'prefix' => env('MEILISEARCH_PREFIX', ''),
    'allowed_indexes' => env('MEILISEARCH_ALLOWED_INDEXES', ''),

    'http_search' => [
        'allowed_indexes' => env('MEILISEARCH_ALLOWED_INDEXES', ''),
        'public_indexes' => env('MEILISEARCH_PUBLIC_INDEXES', ''),
        'E_NAME', 'search'),
    ],

    'batch' => [
        'size' => (int) env('MEILISEARCH_BATCH_SIZE', 500),
        'timeout' => (int) env('MEILISEARCH_BATCH_TIMEOUT', 30),
    ],

    'soft_delete' => (bool) env('MEILISEARCH_SOFT_DELETE', true),

    'search' => [
        'limit' => (int) env('MEILISEARCH_DEFAULT_LIMIT', 20),
        'attributes_to_highlight' => ['*'],
        'highlight_pre_tag' => '<em>',
        'highlight_post_tag' => '</em>',
    ],
];



namespace App\Models;

use Glueful\Database\ORM\Model;
use Glueful\Extensions\Meilisearch\Contracts\SearchableInterface;
use Glueful\Extensions\Meilisearch\Model\Searchable;

class Post extends Model implements SearchableInterface
{
    use Searchable;

    protected string $table = 'posts';

    /**
     * Customize the data indexed in Meilisearch.
     */
    public function toSearchableArray(): array
    {
        return [
            'id' => $this->uuid,
            'title' => $this->title,
            'body' => $this->body,
            'author_name' => $this->author->name ?? null,
            'tags' => $this->tags->pluck('name')->toArray(),
            'category' => $this->category?->name,
            'status' => $this->status,
            'published_at' => $this->published_at?->timestamp,
        ];
    }

    /**
     * Define filterable attributes.
     */
    public function getSearchableFilterableAttributes(): array
    {
        return ['status', 'category', 'tags', 'author_name', 'published_at'];
    }

    /**
     * Define sortable attributes.
     */
    public function getSearchableSortableAttributes(): array
    {
        return ['published_at', 'title'];
    }

    /**
     * Define attributes Meilisearch may return to search callers.
     */
    public function getSearchableDisplayedAttributes(): array
    {
        return ['id', 'title', 'body', 'author_name', 'tags', 'category', 'status', 'published_at'];
    }

    /**
     * Only index published posts.
     */
    public function shouldBeSearchable(): bool
    {
        return $this->status === 'published';
    }
}

'http_search' => [
    'allowed_indexes' => ['posts'],
    'server_filters' => [
        'posts' => 'tenant_uuid = "{claims.tenant_uuid}"',
    ],
    'retrievable_attributes' => [
        'posts' => ['id', 'title', 'excerpt'],
    ],
],

// $context is an ApplicationContext instance
// Simple search
$results = Post::search($context, 'laravel tutorial')->get();

// Access results
foreach ($results as $post) {
    echo $post->title;
}

// Get raw hits without model hydration
$rawResults = Post::search($context, 'laravel')->raw();

// Single filter
$results = Post::search($context, 'php')
    ->where('status', 'published')
    ->get();

// Multiple filters
$results = Post::search($context, 'api')
    ->where('status', 'published')
    ->where('published_at', '>=', strtotime('-30 days'))
    ->whereIn('category', ['tutorials', 'guides'])
    ->get();

// Using raw filter syntax
$results = Post::search($context, 'docker')
    ->filter('status = "published" AND category IN ["tutorials", "guides"]')
    ->get();

$results = Post::search($context, 'api design')
    ->orderBy('published_at', 'desc')
    ->get();

// Multiple sort criteria
$results = Post::search($context, '')
    ->orderBy('category', 'asc')
    ->orderBy('published_at', 'desc')
    ->get();

$results = Post::search($context, 'docker')
    ->where('status', 'published')
    ->paginate(page: 1, perPage: 15);

// Access pagination metadata
$meta = $results->paginationMeta();
// ['current_page' => 1, 'per_page' => 15, 'total' => 42, 'total_pages' => 3, 'has_more' => true]

$results = Post::search($context, '')
    ->facets(['category', 'tags', 'author_name'])
    ->where('status', 'published')
    ->get();

// Access facet distribution
$categoryFacets = $results->facets('category');
// ['tutorials' => 45, 'guides' => 23, 'news' => 12]

// All facets
$allFacets = $results->facets();

class Store extends Model implements SearchableInterface
{
    use Searchable;

    public function toSearchableArray(): array
    {
        return [
            'id' => $this->uuid,
            'name' => $this->name,
            '_geo' => [
                'lat' => (float) $this->latitude,
                'lng' => (float) $this->longitude,
            ],
        ];
    }

    public function getSearchableFilterableAttributes(): array
    {
        return ['_geo', 'category'];
    }

    public function getSearchableSortableAttributes(): array
    {
        return ['_geo', 'name'];
    }
}

// Find stores within 5km of a point
$results = Store::search($context, 'coffee')
    ->whereGeoRadius(40.7128, -74.0060, 5000)
    ->get();

// Find within bounding box
$results = Store::search($context, '')
    ->whereGeoBoundingBox([45.0, -73.0], [40.0, -74.0])
    ->get();

// Sort by distance (nearest first)
$results = Store::search($context, 'coffee')
    ->orderByGeo(40.7128, -74.0060, 'asc')
    ->get();

$results = Post::search($context, 'important topic')
    ->highlight(['title', 'body'])
    ->get();

// Access highlighted results in raw hits
$rawResults = Post::search($context, 'important')->highlight(['title'])->raw();
foreach ($rawResults['hits'] as $hit) {
    echo $hit['_formatted']['title']; // Contains <em>important</em>
}

// Index a single model
$post = Post::find($context, $uuid);
$post->searchableSync();

// Remove from index
$post->searchableRemove();

// Batch indexing via BatchIndexer
$indexer = app($context, BatchIndexer::class);
$posts = Post::query($context)->where('status', 'published')->get();
$indexer->indexMany($posts);

use Glueful\Extensions\Meilisearch\Indexing\IndexManager;

$manager = app($context, IndexManager::class);

// Create index with settings
$manager->createIndex('posts');

// Update index settings
$manager->updateSettings('posts', [
    'filterableAttributes' => ['status', 'category'],
    'sortableAttributes' => ['published_at', 'title'],
    'searchableAttributes' => ['title', 'body', 'tags'],
]);

// Sync settings from model
$manager->syncSettingsForModel(new Post([], $context));

// Get index statistics
$stats = $manager->getStats('posts');

// Delete all documents from index
$manager->flush('posts');

// Delete the index entirely
$manager->deleteIndex('posts');

// Using db() helper with transaction()
db($context)->transaction(function () use ($context) {
    $post = Post::create($context, [
        'title' => 'New Post',
        'body' => 'Content here...',
    ]);
    // Indexing is deferred, not executed yet
});
// After commit, the post is indexed

// If transaction rolls back, nothing is indexed
try {
    db($context)->transaction(function () use ($context) {
        $post = Post::create($context, ['title' => 'Will be rolled back']);
        throw new \Exception('Rollback!');
    });
} catch (\Exception $e) {
    // Transaction rolled back - post is NOT indexed
}
bash
composer extensions:enable meilisearch
bash
php glueful extensions:list
php glueful extensions:info meilisearch
bash
php glueful queue:work --queue=search