Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Search Module Laravel Package

anomaly/search-module

A search module for the Anomaly CMS that adds site-wide search across your modules, with indexing, configurable results, and a simple integration for Laravel-based Anomaly applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm you actually need it. The module is normally pulled in transitively by anomaly/streams-platform (and therefore PyroCMS 3), so check composer show anomaly/search-module before adding it. If it is missing, composer require anomaly/search-module, then install it the Streams way (php artisan addon:install anomaly/search-module --seed — confirm the exact slug via php artisan list | grep addon, since the command signature varies between Streams versions). Migrations create a single flat search table, which is the whole index.

Where to look first, in this order:

  1. vendor/anomaly/search-module/src/Search/ — the repository, criteria/builder objects, SearchModel, and the SearchableInterface/SearchableTrait pair. This is the actual API surface you will code against; the class names below follow the v1.x layout, so verify them here before committing to any snippet.
  2. src/Http/Controller/Admin/SearchController.php and resources/views/ — the out-of-the-box admin UI at /admin/search, and the templates you will likely override.
  3. resources/config/ and routes.php — the route slug (/search on the front end), the admin route, and any view/asset paths you can republish.

First useful use case: log into the control panel, open Search from the sidebar, and type a phrase that you know exists in a stream entry. If results appear, the indexer is already wired to Streams and you only need to (a) style/route a front-end form or (b) make your own custom models searchable. If results do not appear, you have an indexing/publish-state problem, not a search problem — go straight to the search table and SELECT * FROM search LIMIT 10 before touching any code. That single query answers 80% of "search is broken" tickets.

Implementation Patterns

Use the repository, not the Eloquent model, from application code. The module exposes a SearchRepositoryInterface (bound in the service provider) plus an immutable-ish SearchCriteria object. Resolve it out of the container so you inherit pagination, limits, and any locale/visibility scoping the module already applies:

use Anomaly\SearchModule\Search\Contract\SearchRepositoryInterface;
use Anomaly\SearchModule\Search\SearchCriteria;

public function results(Request $request, SearchRepositoryInterface $search)
{
    $criteria = (new SearchCriteria())
        ->setTerm($request->get('q'))
        ->setLimit(20)
        ->setOffset((int) $request->get('page', 1) - 1);

    return view('search.results', [
        'results'  => $search->findAll($criteria), // confirm method name against the interface
        'criteria' => $criteria,
    ]);
}

Treat the exact method names on the repository (findAll, search, getResults — they differ across tags) as something to read off the interface rather than guess.

Make your own models searchable. Implement SearchableInterface and use SearchableTrait; the trait hooks model save/delete so index rows stay in sync. The important part is toSearch() — the index is denormalized, so flatten relationships and strip markup here, not at query time:

class Product extends Model implements SearchableInterface
{
    use SearchableTrait;

    public function toSearch(): array
    {
        return [
            'title'       => $this->name,
            'description' => strip_tags($this->description),
            'keywords'    => $this->tags->pluck('name')->implode(' '),
            'url'         => route('products.show', $this->slug),
        ];
    }
}

Twig integration. The module ships a search plugin/tag namespace. A minimal front-end block looks like:

<form action="/search" method="get">
  <input type="search" name="q" value="{{ request('q') }}">
</form>

{% for result in search_results(term=request('q'), limit=10) %}
  <a href="{{ result.url }}">{{ result.title }}</a>
  <p>{{ result.description }}</p>
{% endfor %}

If search_results is not registered in your build, fall back to the module's own plugin ({{ search:results term=request('q') }}) or simply render the module's /search route in an <iframe>-free form — the controller already returns a results view you can style.

Override, don't fork. Publish the addon's assets (php artisan addon:publish anomaly/search-module) and override views at the theme level or with a module-level view override registered in a service provider, so a future composer update does not clobber your templates.

Integration tips: add the search form as a partial included from your main layout; wire q through your site's locale prefix if you run multi-language; and if you have a queue, push reindexing to a job rather than doing it in the request cycle for bulk imports.

Gotchas and Tips

It is a LIKE %term% index, not a search engine. There is no relevance ranking, no stemming, no fuzzy matching. Multi-word queries and long content tables will degrade quickly; add an index on the title/keywords columns and cap result sets. If you need ranking, this package is the wrong tool — you will end up bolting on Scout/Meilisearch anyway.

Stale and orphaned rows are the default failure mode. Rows are written at index time, so content edited outside the model events (raw SQL imports, queue workers bypassing observers, direct Streams table writes) silently drifts. After any bulk import, plan on a full reindex — look for a search:* artisan command in php artisan list, and if none exists, truncate search and re-save entries programmatically. Deletes that skip model events leave dead links in results.

URLs are baked into the index. Moving the site's domain, changing a route slug, or switching locale prefixes produces results that 404 until you reindex. Store relative paths in toSearch() if your links are likely to move.

Visibility is your responsibility. The flat index has no concept of "published", "private", or per-role access unless your toSearch() or criteria filter for it. A draft product indexed by the trait will surface on the public /search page. Filter on publish state when building the index, and never rely on the front-end controller to hide anything. Test with a known-unpublished entry.

Small-project risk. Zero stars and no dependents means effectively no community triage: pin the version in composer.json, and be prepared to read the source rather than search for answers. Check the composer.json constraints against your Laravel/Streams version before upgrading either — this is the most common way the addon breaks.

Debugging checklist, in order: (1) SELECT from the search table — is the content even indexed? (2) Does the term match on a single word with no punctuation? (3) Dump the SearchCriteria and the compiled query. (4) Confirm the view override is actually the one rendering (clear compiled views: php artisan view:clear). (5) Check route registration if the front-end page is silent — the module's route may be shadowed by a theme or catch-all route.

Extension points worth knowing: the repository binding (swap it for a custom implementation), the results view, the SearchableTrait events, and the form/table builders used by the admin UI. Those four cover almost every customization request without forking the package.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource