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 Ec Validator Laravel Package

tavo1987/laravel-ec-validator

Laravel validation rules for Ecuadorian identification documents, letting you verify cédula and RUC numbers with ready-made rules and checksum checks so forms and APIs accept only correctly formatted IDs.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm you're compatible first. The package requires PHP ^8.2 and Laravel ^11.0|^12.0 — it will simply refuse to install on Laravel 10, and there is no graceful degradation path.

composer require tavo1987/laravel-ec-validator

There is nothing to publish — no config file, no lang files, no migrations. The entire footprint is two classes:

src/EcValidatorServiceProvider.php   # auto-discovered, registers the resolver
src/LaravelValidatorEc.php           # extends Illuminate\Validation\Validator

Where to look first. Open src/LaravelValidatorEc.php and read validateEcuador(). It parses $parameters[0] and maps it to a method on the core library Tavo\ValidadorEc:

Rule string Meaning Delegate
ecuador:ci Cédula (10 digits) validateCedula()
ecuador:ruc RUC, natural person validateNaturalPersonRuc()
ecuador:ruc_spub RUC, public entity validatePublicCompanyRuc()
ecuador:ruc_spriv RUC, private company validatePrivateCompanyRuc()

Also read tests/CustomValidationRulesTest.php — it contains the only authoritative set of valid/invalid fixtures the author ships (e.g. 0926687856 is a valid CI there). Steal those strings for your own test vectors before you trust the rules with production data.

Your first use case is almost always a signup form where the user picks an identification type and then types a number. Smoke-test it before wiring anything up:

// php artisan tinker
$v = Validator::make(['ci' => '0926687856'], ['ci' => 'ecuador:ci']);
$v->passes();      // true
$v->errors()->all(); // inspect the message text here — see Gotchas

Implementation Patterns

FormRequest with a type selector

Because there is no "any RUC" rule, the type selector drives which rule applies. Use Rule::when() rather than trying to OR rules together:

use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        'tipo_identificacion' => ['required', Rule::in(['ci', 'ruc', 'ruc_spub', 'ruc_spriv'])],
        'identificacion' => [
            'required',
            'string',
            Rule::when($this->input('tipo_identificacion') === 'ci', ['size:10', 'ecuador:ci']),
            Rule::when($this->input('tipo_identificacion') === 'ruc', ['size:13', 'ecuador:ruc']),
            Rule::when($this->input('tipo_identificacion') === 'ruc_spub', ['size:13', 'ecuador:ruc_spub']),
            Rule::when($this->input('tipo_identificacion') === 'ruc_spriv', ['size:13', 'ecuador:ruc_spriv']),
        ],
    ];
}

The size:10 / size:13 guards are cheap and make failures readable ("must be 10 characters") instead of a generic "invalid".

Normalize before validating

The rules are strict about digits — spaces, dots and dashes will fail. Clean the input first, and do it in the FormRequest so the normalized value is also what gets persisted:

protected function prepareForValidation(): void
{
    $this->merge([
        'identificacion' => preg_replace('/[^0-9]/', '', (string) $this->input('identificacion')),
    ]);
}

This also matters for unique rules: put the uniqueness check after normalization in the pipeline, otherwise you compare a formatted string against clean DB values and miss duplicates.

Derive the type from the number instead of asking

If the third digit is 0–5 it's a natural-person RUC, 6 indicates a public entity, and 9 a private company/society. A small classifier keeps your UI simpler than an extra dropdown:

protected function rules(): array
{
    $id = $this->input('identificacion', '');
    $suffix = strlen($id) === 13 ? match ($id[2]) {
        '6' => 'ruc_spub',
        '9' => 'ruc_spriv',
        default => 'ruc',
    } : 'ci';

    return ['identificacion' => ['required', 'string', "ecuador:{$suffix}"]];
}

Collections, imports and arrays

The rules compose like any other rule, including wildcards — useful for CSV/bulk importers where you collect per-row errors with indices intact:

'rows.*.identificacion' => ['required', 'ecuador:ci'],

For an importer, prefer Validator::make() per row over $request->validate() so one bad row doesn't abort the whole batch and you can attach rows.12.identificacion to the report.

Livewire / Filament / Nova

It's a plain Laravel rule, so it works anywhere Laravel's validator is used. Filament:

TextInput::make('ruc')
    ->rules(['required', 'ecuador:ruc_spriv'])
    ->validationMessages(['ecuador' => 'El RUC de la empresa no es válido.']),

In Livewire, keep the normalization in updatedIdentificacion() or updated($name) so the field is clean before the rules run on updated()/submit().

Wrap it in a ValidationRule object for reuse

The string-rule form gives you no type safety, no IDE completion, and a fatal Error on typos. For anything used in more than one or two places, wrap it once — you get a proper ValidationException, a message you own, and something you can unit test directly:

final class EcuadorianId implements ValidationRule
{
    public function __construct(private string $type = 'ci') {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $checker = Validator::make(
            ['id' => $value],
            ['id' => "ecuador:{$this->type}"]
        );

        if ($checker->fails()) {
            $fail('messages.' . $this->type)->translate();
        }
    }
}

Call as new EcuadorianId('ruc_spriv'). The underlying Tavo\ValidadorEc methods can also be called from a bare Rule::create closure if you'd rather drop the rule-string parsing layer entirely.

Custom messages — do this on day one

Add explicit messages in each FormRequest (or in a shared base request). Do not rely on defaults:

public function messages(): array
{
    return [
        'identificacion.ecuador' => 'El número de identificación no es válido.',
    ];
}

Note the key: the rule name is ecuador, the parameter (ci, ruc, …) is not part of the message key. You can't easily produce per-type messages via the messages() array — if you need "La cédula no es válida" vs. "El RUC no es válido", use the wrapper class above or Rule::when with distinct attribute names.


Gotchas and Tips

  • The default failure message leaks a translation key. The package ships no resources/lang and no validation.ecuador entry, so $validator->errors()->first() will be validation.ecuador (or a raw key string), not a sentence. This is the single most common surprise. Always supply messages() / validationMessages().
  • An unknown parameter throws a fatal Error. 'ecuador:rcu' (typo) or a bare 'ecuador' (missing parameter) throws \Error from the rule, not a validation failure — you get a 500 in production instead of a field error. Never build the rule string from unvalidated user input; whitelist the type with Rule::in() first, as shown above.
  • Empty strings pass. The rule is not implicit, so null/'' will validate successfully unless you also add required. Classic trap on optional profile fields.
  • There is no "any RUC" rule. ecuador:ruc is natural persons only. A private company RUC will fail it. Validate the type explicitly or wrap the core library and accept any of the three RUC checks.
  • Ecuadorian pasaporte and foreign IDs are unsupported. Every rule expects 10 or 13 digits. If your form serves non-residents, exclude them from this rule set entirely rather than letting the validator reject them.
  • Validator::resolver() is a global hook — it can be clobbered. The service provider registers a custom validator class. If another installed package does the same (there are several LATAM/ID validation packages that do), the last registration wins and one package's rule silently disappears. If you see "Method ... does not exist" or validation.ecuador failing inexplicably, check composer show for competing validator-resolver packages and register whichever you need manually in your AppServiceProvider.
  • The rule name ecuador is generic. Any in-house rule of the same name will collide. Grep your app for 'ecuador' before installing.
  • Debug with the errors bag, not passes(). $v->errors()->toArray() shows you the key path and the raw message; --filter valid_ci against the package's own test suite tells you whether a failure is your data or the library.
  • Test fixtures, not intuition. Cédula/RUC check digits are mod-10/mod-11 variants and it's easy to assume a real-looking number is valid. Build a fixture list: valid CI, invalid check digit, valid natural RUC, public RUC, private RUC, 9 digits, 14 digits, empty, formatted-with-dashes.
  • Version pinning matters here. 6 stars, 0 dependents, one maintainer, and a release date that reads as future-dated in the listings. Run composer show tavo1987/laravel-ec-validator --all, pin an exact version in composer.json, and commit composer.lock. Because the MIT-licensed surface is two small classes, vendoring the logic (or calling Tavo\ValidadorEc directly and dropping the Laravel wrapper) is a genuinely low-cost exit strategy if the package goes stale.
  • Extension points are minimal by design. No config, no driver, no container bindings to swap. Your leverage is (a) the message array, (b) wrapping it in a ValidationRule, or (c) bypassing it and using the underlying tavo1987/ec-validador-cedula-ruc library directly — which gives you typed booleans and real method names instead of magic strings.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums