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

Sanigen Laravel Package

ivanbaric/sanigen

Sanigen is a Laravel package for defining and generating reusable sanitizers that clean and normalize incoming request data before validation or persistence. It helps keep form and API input consistent, predictable, and safe with a simple, rule-based workflow.

View on GitHub
Deep Wiki
Context7

Getting Started

Sanigen 1.9.0 is a sanitization layer, not a validator — it cleans values before they hit your database, so it sits before validation and before casting. Get it wired once and it runs on every save without further ceremony.

  1. Install and publish config:
composer require ivanbaric/sanigen
php artisan vendor:publish --tag=sanigen-config
  1. Look at config/sanigen.php first. In 1.9 the important keys are the traversal limits (depth, item count, scalar length) — these are now enforced across a whole root traversal, so they're your primary safety valve for nested JSON. Also confirm the sanitizer alias list there; plain_text and safe_html are the two you'll use most.

  2. Look second at the model-facing trait and the sanitizeAttributes() entry point. That method is the documented seam — if you need to sanitize something outside Eloquent, call it directly rather than reaching for internals.

  3. First use case — a plain string attribute:

use IvanBaric\Sanigen\Concerns\SanitizesAttributes;

class Post extends Model
{
    use SanitizesAttributes;

    protected array $sanitizers = [
        'title' => 'plain_text',
        'body'  => 'safe_html',
    ];
}

Save a Post with <script> in the body and confirm it's stripped on refresh(). If that works, the rest is configuration.

Implementation Patterns

Rely on automatic recursion; don't hand-write levels. The headline 1.9 change is that root rules recurse on their own. A rule on meta now walks the entire array/object tree beneath it, preserving keys, structure, integers, floats, booleans, and nulls. Writing meta.address.line1 and meta.address.line2 separately is now redundant noise.

Use dot-notation and wildcards only for heterogeneous JSON. Where a subtree genuinely has different shapes per key, target them explicitly:

protected array $sanitizers = [
    'meta'           => 'plain_text',
    'meta.*.url'     => 'url_clean',
    'meta.*.caption' => 'plain_text',
    'blocks.*.html'  => 'safe_html',
];

Most-specific path wins, and the compiled engine resolves this in a single pass with deterministic precedence — so ordering in the array doesn't change the outcome.

Spatie Translatable is a first-class path now. Swap the trait:

use IvanBaric\Sanigen\Concerns\HasSanitizedTranslations;

class Page extends Model
{
    use HasSanitizedTranslations;
    public array $translatable = ['title', 'body'];
}

Each locale string gets its own pipeline, so a bad de value can't affect en.

Sanitize at the write boundary, not in controllers. Attach rules to the model and let every save path — forms, API resources, seeders, imports, jobs — inherit them. One sanitizeAttributes() call per top-level root is guaranteed, so nested saves and saveQuietly() don't double-process.

Prefer aliases over bespoke regex. The built-in pipelines already handle Unicode NFC normalization and newline normalization that hand-rolled preg_replace calls will get wrong.

Gotchas and Tips

  • Drop the recursive: prefix. It still works in 1.9 but is deprecated and slated for removal in a major. Grep your rule strings for recursive: and flatten them now — behaviour is identical, so this is a safe mechanical change.
  • Don't expect strings back. Integers stay integers, floats stay floats, booleans stay booleans, nulls stay null. If downstream code does strlen($model->meta['count']), that's your bug, not Sanigen's.
  • The safety limits are a shared budget per root traversal. A deeply nested blocks array with many items can consume the depth/item allowance and cause later siblings to be skipped. If large payloads are silently losing entries, raise the config limits deliberately rather than assuming a sanitizer is broken.
  • Fail-closed is real. Objects, resources, malformed paths, and equal-specificity conflicts are dropped rather than passed through. That's the right default, but it means a typo'd wildcard path doesn't throw — it quietly does nothing. Cross-check rule paths against a fixture in tests.
  • Avoid equal-specificity conflicts. Two patterns matching the same leaf at the same specificity resolve as a conflict and the value is dropped. Keep rule paths distinct rather than relying on one beating the other.
  • Exceptions are value-free by design. You won't see submitted content in stack traces — good for PII, awkward for debugging. Log a redacted copy of the raw input next to the exception yourself.
  • Upgrade for the fixes if you handle money or markup. v1.9.0 fixes negative decimals losing their minus sign (a genuine data-corruption bug for prices and coordinates), plain_text mishandling Windows and old-Mac line endings, and same-name aliases like safe_html failing to delegate to their base sanitizer. Anything below 1.9 should be considered unsafe for currency fields.
  • NFC normalization changes bytes, not appearance. Snapshot tests comparing raw string hashes or byte length will start failing after the upgrade even though output looks identical. Assert on semantics, not on md5().
  • Test structure, not order. Structured output preserves keys but shouldn't be compared with assertSame against a hand-built array unless you control key order upstream — use assertEquals on the decoded structure.
  • Extension point: custom sanitizers registered by name are subject to the same precedence and depth rules as built-ins, so give them unambiguous, path-specific names to avoid triggering conflict fail-closed behaviour.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core