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

Wn Search Plugin Laravel Package

winter/wn-search-plugin

Add site-wide search to Winter CMS with this plugin. Index pages and supported content models, run queries through a ready-to-use search component, and render paginated results in your theme. Configurable indexes and providers make integration straightforward and extensible.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and update

composer require winter/wn-search-plugin
php artisan winter:up

winter:up runs the plugin's migrations (it creates the shared search_index table) and applies pending updates. Confirm the plugin row exists under Settings → System → Updates before debugging anything else.

Where to look first

Inside plugins/winter/search/:

  • Plugin.php — registers the search component, the Searchable model behavior, the backend settings page, and any console commands.
  • classes/ — the search manager / driver abstraction. This is the seam you will interact with most.
  • drivers/ — the concrete backends (a database driver ships as the safe default; other drivers may need extra composer packages).
  • components/Search.php + components/search/*.htm — the frontend component and its default markup. Read default.htm first; it is the best documentation of handler names, property names, and partial aliases.
  • behaviors/Searchable.php — how models get indexed.

Backend config

Go to Settings → Search (or the plugin's settings page) and pick a driver, set results per page, and note whether indexing is synchronous on save or requires a rebuild. If no settings page appears, the driver is configured in config/ or via .env — check Plugin.php for registerSettings.

First use case

Index one model and wire up one page:

class Post extends Model
{
    public $implement = [\Winter\Search\Behaviors\Searchable::class];

    // fields to push into the index — verify the exact property/hook
    // name against Behaviors/Searchable.php in your installed version
    public $searchable = ['title', 'excerpt', 'body'];
}

Then on a CMS page:

[search]
resultsPerPage = 10
==
<form data-request="onSearch" data-request-update="'@results': '#search-results'">
    <input type="text" name="q" value="{{ searchTerm }}">
    <button type="submit">Search</button>
</form>

<div id="search-results">
    {% partial '@results' %}
</div>

Re-save a Post in the backend (or run the index/rebuild command) and search for a word you know is in a title. If you get nothing, the indexing path is the problem, not the query.

Implementation Patterns

Behavior-on-model, index-table-as-source-of-truth. The behavior writes rows into a central index table keyed by model class + record ID. Treat that table as a denormalised projection: it is disposable and rebuildable, never authoritative. Anything you need to filter or display should either live in the index payload or be re-fetched from the source model.

Fluent manager over the component. For non-trivial needs, skip the component and talk to the manager directly in a controller, console command, or a custom component:

$results = Search::driver('database')
    ->search($term)
    ->where('model', Post::class)   // scope to one model type
    ->paginate(10);

This composes cleanly: add where() clauses for scoping, then hand the paginator to your own partial. It is the right layer for JSON APIs and for search pages that need more than the stock component offers.

Indexing strategy. Decide early between three modes:

  1. Sync on save — simplest, zero moving parts, fine up to a few thousand records.
  2. Queued indexing — override the behavior's index hook or listen to model events and dispatch a job; necessary once saves start hitting the index table hard.
  3. Scheduled full rebuild — a console command in the scheduler, nightly. Always have this even if you also do 1 or 2; it is your recovery path.

Search UI as a progressive enhancement. The pattern that ages well: a plain GET form that works without JS, plus an onSearch AJAX handler that updates only the results partial. Put the term in the query string (?q=...) so results are shareable and so you can hydrate the input on onStart/page load.

Multi-model search. Keep one index and filter by model class for scoped searches; omit the where('model', ...) and group/annotate by model when you want a unified "site search" results page. Render each result through a small per-model presenter (a toSearchResult() method or a map of model → partial) rather than branching in Twig.

Driver abstraction as a scaling dial. Start on the database driver, keep all query construction behind your own service or component wrapper, and swap drivers later by changing config plus one adapter class. This is the main architectural payoff of the package; don't leak driver-specific query syntax into page templates.

Gotchas and Tips

  • The index silently misses data. Bulk imports, DB::table()->insert(), seeders, and direct SQL all bypass model events and therefore the index. Any workflow that writes outside Eloquent needs an explicit reindex step. Symptom: search works in dev, returns stale/empty results after a deploy that imported content.
  • Visibility is your problem, not the index's. Draft, unpublished, soft-deleted, and permission-gated records usually still have index rows. Filter at query time (->where('status', 'published') or post-filter by fetching models and applying scopes) and never rely on the index to enforce access control. Re-fetching the source model for the detail view and re-checking permissions is the safe pattern.
  • The database driver is LIKE '%term%'. No relevance ranking, and a leading wildcard defeats indexes, so it degrades linearly. Acceptable for a blog; not for tens of thousands of rows. Rank results yourself (title matches before body matches) if ordering matters.
  • Minimum word length and stopwords. Short terms, common words, and punctuation can legitimately return zero results depending on the driver and the underlying engine. Test with a distinctive multi-character term before concluding the plugin is broken.
  • Escape user input for the driver. Strip control characters, cap term length, and be careful passing raw input to any MATCH ... AGAINST or Lucene-ish syntax — a stray quote or operator is either a SQL error or an unexpected filter.
  • Reindex command is your debugger. When results look wrong, rebuild the index once before investigating the query. If the rebuild fixes it, the bug is in your save path. If it doesn't, the bug is in your toSearchableArray() payload.
  • Partial overrides live in the theme. Copy the component's default markup into your theme's partials rather than editing plugins/ — plugin files get overwritten on composer update. Backend-editable component partials (CMS → Partials) are the supported extension point.
  • Cache keys need the term and the context. If you cache result sets, include the normalised search term, page number, locale, and any active scope in the key. Multi-lingual sites that cache on term alone serve the wrong language.
  • Winter, not October. The namespace is Winter\Search. October CMS plugins using October\ namespaces won't drop in cleanly; if you're porting, check for October\Rain references and a translations compatibility layer.
  • Pin the version. With a small user base and a low star count, upstream fixes are infrequent and regressions may sit unreported. Lock a known-good version in composer.json, keep a fork or patch set handy, and read the driver source before upgrading — you are effectively your own first line of support here.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect