{"id":588,"date":"2025-09-01T11:51:54","date_gmt":"2025-09-01T11:51:54","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=588"},"modified":"2025-09-01T11:51:56","modified_gmt":"2025-09-01T11:51:56","slug":"how-to-build-a-search-function-in-laravel-with-scout-and-meilisearch","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/how-to-build-a-search-function-in-laravel-with-scout-and-meilisearch\/","title":{"rendered":"How to Build a Search Function in Laravel with Scout and Meilisearch"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\"><strong>How to Build a Search Function in Laravel with Scout and Meilisearch<\/strong><\/h1>\n\n\n\n<p>Modern applications need fast, typo-tolerant, and relevant search. Laravel provides <strong>Scout<\/strong>, a driver-based abstraction for full-text search engines, and <strong>Meilisearch<\/strong>, a blazing fast open-source search engine. Together, they make it simple to build advanced search into your Laravel app with minimal effort. In this guide, we\u2019ll explain what Scout and Meilisearch are, why they work well together, and how to set up a search feature with Blade UI and JSON API responses.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What is Laravel Scout?<\/strong><\/h2>\n\n\n\n<p><strong>Laravel Scout<\/strong> is an official Laravel package that provides a simple, driver-based abstraction for full-text search. Instead of writing raw queries against search engines, Scout lets you call methods like <code>Model::search()<\/code> directly on your Eloquent models. Behind the scenes, Scout syncs your models with the search index (Meilisearch, Algolia, Elasticsearch, etc.).<\/p>\n\n\n\n<p><strong>Why use it?<\/strong> Because Scout handles indexing, updating, and deleting records automatically. It integrates seamlessly with Eloquent, so you can focus on building features instead of writing search infrastructure code.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What is Meilisearch?<\/strong><\/h2>\n\n\n\n<p><strong>Meilisearch<\/strong> is an open-source, fast, and developer-friendly search engine. It provides features like typo tolerance, filters, relevancy ranking, and near-instant search results. It\u2019s lightweight, easy to host, and has great Laravel integration via Scout.<\/p>\n\n\n\n<p><strong>Why Meilisearch?<\/strong> Unlike SQL <code>LIKE<\/code> queries, Meilisearch is designed for real-time search experiences, supports autocomplete, and ranks results by relevancy rather than simple string matching. Perfect for blogs, e-commerce, and SaaS dashboards.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Why Use Scout with Meilisearch?<\/strong><\/h2>\n\n\n\n<p>Scout acts as a bridge between Laravel and Meilisearch. Instead of learning Meilisearch\u2019s HTTP API directly, you work with clean Eloquent methods. Scout automatically syncs your models to Meilisearch whenever they\u2019re created, updated, or deleted.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Without Scout<\/strong>: You would need to call Meilisearch\u2019s HTTP API manually for indexing and updates.<\/li>\n<li><strong>With Scout<\/strong>: Just call <code>Post::search('query')<\/code> and get results instantly, with Eloquent models returned.<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Install and Configure Scout + Meilisearch<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">composer require laravel\/scout meilisearch\/meilisearch-php<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Publish the Scout config file:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">php artisan vendor:publish --provider=<span class=\"hljs-string\">\"Laravel\\Scout\\ScoutServiceProvider\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Update your <code>.env<\/code> file to use Meilisearch:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">SCOUT_DRIVER=meilisearch\nMEILISEARCH_HOST=http:\/\/127.0.0.1:7700\nMEILISEARCH_KEY=masterKey<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Run Meilisearch locally via Docker for development:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">docker run -it --rm -p 7700:7700 getmeili\/meilisearch:latest<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Prepare Your Model for Search<\/strong><\/h2>\n\n\n\n<p>Add the <code>Searchable<\/code> trait to your Eloquent model. This tells Scout to sync it with Meilisearch.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Models\/Post.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Eloquent<\/span>\\<span class=\"hljs-title\">Model<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Laravel<\/span>\\<span class=\"hljs-title\">Scout<\/span>\\<span class=\"hljs-title\">Searchable<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Post<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Model<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Searchable<\/span>;\n\n    <span class=\"hljs-keyword\">protected<\/span> $fillable = &#91;<span class=\"hljs-string\">'title'<\/span>, <span class=\"hljs-string\">'content'<\/span>];\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">toSearchableArray<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">array<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> &#91;\n            <span class=\"hljs-string\">'title'<\/span> =&gt; <span class=\"hljs-keyword\">$this<\/span>-&gt;title,\n            <span class=\"hljs-string\">'content'<\/span> =&gt; <span class=\"hljs-keyword\">$this<\/span>-&gt;content,\n        ];\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Scout will automatically keep your search index in sync whenever a Post is created, updated, or deleted.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Build a Search Controller and Blade Form<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/SearchController.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Post<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Request<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">SearchController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n    <\/span>{\n        $query = $request-&gt;input(<span class=\"hljs-string\">'q'<\/span>);\n        $results = $query ? Post::search($query)-&gt;get() : collect();\n\n        <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'search.index'<\/span>, compact(<span class=\"hljs-string\">'results'<\/span>, <span class=\"hljs-string\">'query'<\/span>));\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;!-- resources\/views\/search\/index.blade.php --&gt;\n&lt;form method=<span class=\"hljs-string\">\"GET\"<\/span> action=<span class=\"hljs-string\">\"\/search\"<\/span>&gt;\n  &lt;input type=<span class=\"hljs-string\">\"text\"<\/span> name=<span class=\"hljs-string\">\"q\"<\/span> value=<span class=\"hljs-string\">\"{{ $query }}\"<\/span> placeholder=<span class=\"hljs-string\">\"Search posts...\"<\/span>&gt;\n  &lt;button type=<span class=\"hljs-string\">\"submit\"<\/span>&gt;Search&lt;\/button&gt;\n&lt;\/form&gt;\n\n&lt;ul&gt;\n  @<span class=\"hljs-keyword\">foreach<\/span>($results <span class=\"hljs-keyword\">as<\/span> $post)\n    &lt;li&gt;&lt;a href=<span class=\"hljs-string\">\"\/posts\/{{ $post-&gt;id }}\"<\/span>&gt;{{ $post-&gt;title }}&lt;\/a&gt;&lt;\/li&gt;\n  @<span class=\"hljs-keyword\">endforeach<\/span>\n&lt;\/ul&gt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This simple Blade template lets users search posts and displays matching results instantly from Meilisearch.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Expose a JSON API for Search<\/strong><\/h2>\n\n\n\n<p>If you\u2019re building a Vue or React frontend, you may want search results as JSON. Add an API endpoint:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/api.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">SearchController<\/span>;\n\nRoute::get(<span class=\"hljs-string\">'\/search'<\/span>, &#91;SearchController::class, <span class=\"hljs-string\">'api'<\/span>]);\n\n<span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/SearchController.php<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">api<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n<\/span>{\n    $query = $request-&gt;input(<span class=\"hljs-string\">'q'<\/span>);\n    $results = $query ? Post::search($query)-&gt;paginate(<span class=\"hljs-number\">10<\/span>) : collect();\n\n    <span class=\"hljs-keyword\">return<\/span> response()-&gt;json($results);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, your JavaScript frontend can fetch <code>\/api\/search?q=keyword<\/code> and render results in real-time with autocomplete or infinite scroll.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Scout + Meilisearch vs SQL LIKE Queries<\/strong><\/h2>\n\n\n\n<table class=\"wp-block-table\"><tbody>\n<tr>\n<td><strong>Feature<\/strong><\/td>\n<td><strong>Scout + Meilisearch<\/strong><\/td>\n<td><strong>Raw SQL LIKE<\/strong><\/td>\n<\/tr>\n<tr>\n<td><strong>Speed<\/strong><\/td>\n<td>Fast, indexed search optimized for full-text<\/td>\n<td>Slow for large datasets<\/td>\n<\/tr>\n<tr>\n<td><strong>Relevancy<\/strong><\/td>\n<td>Results ranked by relevance and typo tolerance<\/td>\n<td>Basic string match only<\/td>\n<\/tr>\n<tr>\n<td><strong>Features<\/strong><\/td>\n<td>Autocomplete, filters, pagination, typo tolerance<\/td>\n<td>No advanced features<\/td>\n<\/tr>\n<tr>\n<td><strong>Integration<\/strong><\/td>\n<td>Clean Eloquent <code>search()<\/code> API<\/td>\n<td>Manual SQL queries<\/td>\n<\/tr>\n<tr>\n<td><strong>Scalability<\/strong><\/td>\n<td>Handles millions of records efficiently<\/td>\n<td>Poor scalability<\/td>\n<\/tr>\n<\/tbody><\/table>\n\n\n\n<p>This table shows why using <strong>Scout with Meilisearch<\/strong> is the modern approach compared to raw SQL <code>LIKE<\/code> queries, especially for content-heavy applications.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Wrapping Up<\/strong><\/h2>\n\n\n\n<p>In this article, you learned what <strong>Laravel Scout<\/strong> and <strong>Meilisearch<\/strong> are, why Scout is used as a bridge to Meilisearch, and how to build a search function in Laravel with both Blade and JSON API examples. Compared to SQL <code>LIKE<\/code> queries, Scout and Meilisearch deliver faster, smarter, and more scalable search results. This setup is perfect for blogs, SaaS apps, and e-commerce platforms where speed and relevancy matter.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What\u2019s Next<\/strong><\/h2>\n\n\n\n<p>Keep building your Laravel search and SEO stack with these related guides:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/implementing-full-text-search-in-laravel-with-mysql\">Implementing Full-Text Search in Laravel with MySQL<\/a><\/li>\n<li><a href=\"\/blog\/creating-json-ld-structured-data-in-laravel-for-seo\">Creating JSON-LD Structured Data in Laravel for SEO<\/a><\/li>\n<li><a href=\"\/blog\/laravel-seo-guide-optimizing-meta-slugs-and-sitemaps\">Laravel SEO Guide: Optimizing Meta, Slugs, and Sitemaps<\/a><\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>How to Build a Search Function in Laravel with Scout and Meilisearch Modern applications need fast, typo-tolerant, and relevant search. Laravel provides Scout, a driver-based abstraction for full-text search engines, and Meilisearch, a blazing fast open-source search engine. Together, they make it simple to build advanced search into your Laravel app with minimal effort. In [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":592,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[36,132,131,129,130],"class_list":["post-588","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-eloquent","tag-full-text","tag-meilisearch","tag-scout","tag-search"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/588","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=588"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/588\/revisions"}],"predecessor-version":[{"id":591,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/588\/revisions\/591"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/592"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=588"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=588"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=588"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}