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

Laravel Mosparo Laravel Package

exeque/laravel-mosparo

Laravel Mosparo integrates the open-source Mosparo spam protection service into Laravel applications. Render the widget, validate form submissions server-side, and block spam with minimal configuration. Supports configurable hosts, credentials, and validation rules for a smooth developer experience.

View on GitHub
Deep Wiki
Context7

Getting Started

Check your platform before anything else. v0.1.0 requires PHP 8.4+, Laravel 13 and — for the Livewire integration — Livewire 4. That is a narrow window; on a Laravel 11/12 app or PHP 8.2/8.3 runtime you cannot install this yet, and there is no fallback branch to pin. Confirm php -v and composer show laravel/framework livewire/livewire first; if you are not there, the whole evaluation stops at step zero.

Install and configure. composer require exeque/laravel-mosparo, then publish the config and fill in your mosparo project credentials (host, project UUID, public key, private key). Keep them in .env, not in the committed config file. With several mosparo projects in one installation, each gets a name and you select one per call site — see below. Set MOSPARO_ENABLED=false in .env.testing and in local .env so nobody fights a live spam service while typing placeholder text into a contact form.

First use case: a contact form. Three moving parts, in order:

{{-- layout head --}}
@mosparoScript

<form method="POST" action="/contact">
    @csrf
    <input name="email">
    <textarea name="message"></textarea>

    <x-mosparo />            {{-- or @mosparo --}}

    @mosparoError('mosparo') @enderror
    <button>Send</button>
</form>

and in the controller, a ValidMosparo rule that names the fields mosparo must have checked:

$request->validate([
    'email'   => ['required', 'email'],
    'message' => ['required', 'string'],
    'mosparo' => [new ValidMosparo(['email', 'message'])],
]);

Where to look first. The docs at exeque.github.io/laravel-mosparo are the primary reference. After that, read ExeQue\Mosparo\Data — the result objects are the shape of everything the facade returns, and knowing them saves you guessing at array keys. Then skim the exception hierarchy: ConnectionFailed, RequestFailed, UnknownProject, IncompleteProject all implement MosparoException, and which one you catch determines whether you fail open or shout.

Implementation Patterns

Plain Blade forms. The pattern above is the whole story for server-rendered posts. @mosparoScript goes once in the layout head (not per form), and @mosparo / <x-mosparo /> goes inside each protected form. The box initialises wherever it appears — first page load, Livewire re-renders, wire:navigate transitions and browser back-button restores — so you generally do not need to re-init anything by hand.

Livewire components. Add InteractsWithMosparo to the component and call $this->mosparo->verify($this->form, ['message']) where $this->form is your Livewire form object and the array is the authoritative field list. The component's box fetches fresh tokens after every check, which is what makes repeated submissions inside one Livewire session work. Display the error with the same @mosparoError directive — it behaves identically for Livewire and plain forms, which means one error partial can serve both.

The field list is the contract. Every check — rule or trait — takes the set of fields you require mosparo to have validated. This is the package's best design decision: it closes the classic hole where an attacker posts a field the client-side box never saw. The cost is that the list must be maintained. Put it in one place (a protected array $mosparoFields on the form object, or a constant next to the form definition) rather than repeating the array at each call site, and treat adding a form field as a two-line change: validation rules and the mosparo list.

Facade for everything non-form. Mosparo::passes() and Mosparo::verify() for imperative checks (API endpoints, queued jobs, custom middleware), Mosparo::statistics() for dashboards, plus rule packages, metadata and access to mosparo's own underlying client when you need something the wrapper doesn't expose. Results come back as read-only data objects — treat them as immutable and map into your own DTOs if you need to persist anything.

Caching. Optional caching of what is read, configurable at three levels: globally in config, per named project, and per call. Use the per-call scope for one-off admin screens that must see live numbers, and leave the global default on for request-path reads. Mosparo::flushCache() and php artisan mosparo:clear-cache clear only mosparo's entries, so they are safe to run in production or from a deploy hook.

Multiple projects. With several mosparo installations (say, one per brand or per regional site), select by project name at the call site. Wire the name from config keyed off your own tenant/brand resolution rather than sprinkling literals through controllers.

Testing. Mosparo::fake() is the reason to prefer this package over calling mosparo's PHP client directly. It supports accept(), reject(), call counts, sequences (first call rejected, second accepted — ideal for "fix your submission" flows), callbacks for conditional behaviour, and assertions. Cover three cases in every protected form's test: accepted, rejected (assert the error message renders), and the disabled path. MOSPARO_ENABLED=false is the blunt instrument — reach for fake() whenever you need assertions, and reserve the env flag for test suites that simply should not touch the integration.

Gotchas and Tips

Version lock-in is the biggest risk. PHP 8.4 + Laravel 13 + Livewire 4 means this package currently targets the leading edge of the ecosystem. If you are evaluating it for an app you cannot upgrade, you are evaluating a future migration, not an install. Track whether the maintainer widens constraints in later releases; a 0.1.x package with hard framework minimums will move.

Decide fail-open vs fail-closed explicitly. ConnectionFailed and RequestFailed mean mosparo is unreachable or unhappy — the spam check did not happen. Catch MosparoException and make a deliberate choice per form: reject the submission with a human message (safe, but you lose real users when mosparo is down) or accept it and log (available, but unprotected). Do not let UnknownProject or IncompleteProject fall into the same handler — those are configuration errors in your own app, and swallowing them means silently accepting every spam submission forever. Log them at error level and fail loudly in non-production.

Token freshness cuts both ways. The box fetching new tokens after each check is what makes Livewire work, but it also means a captured payload is single-use by design. Any retry logic — queued resubmission, HTTP client retries, double-submit on flaky mobile networks — will fail the second attempt. If you need idempotent submission, deduplicate on your side before re-verifying.

Field list drift is the quiet failure mode. A field added to the form but omitted from the mosparo list is not "unprotected but working" — the check names the fields it requires, so a mismatch surfaces as a failed validation, usually only in production where the box is enabled. Add a feature test that submits the full field set through Mosparo::fake()->accept(); it catches the drift at the point you introduce it.

Stale cache after credential rotation. Metadata and project config are cached. After rotating keys or changing project settings in the mosparo admin, run mosparo:clear-cache — otherwise you get IncompleteProject or mysterious auth failures that look like network problems. Put the flush in the same runbook step as the key rotation.

Don't call statistics() inline in a view. It is a remote read; without caching it turns a dashboard render into an external HTTP dependency. Cache it, or push it to a scheduled job that stores the numbers.

Translations ship with 29 European languages. That covers the message shown by @mosparoError out of the box. If your UI is in another language, or you want your own tone ("Your message looked like spam — email us instead"), publish and override the translation files rather than wrapping the directive in custom copy; you keep the package's per-language defaults for everything else.

Read-only data objects are a feature. Resist the urge to mutate results into your own structures in place. Mapping to app-level DTOs keeps the package boundary thin and makes a future swap or version bump a contained change.

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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style