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

Date Extra Validator Bundle Laravel Package

oh/date-extra-validator-bundle

A Symfony bundle adding extra date validation constraints to the standard validator. It enforces additional date checks in forms and entities via familiar constraint annotations, filling gaps when built-in date rules aren't enough. A small, focused package for more flexible date validation.

View on GitHub
Deep Wiki
Context7

Getting Started

Before writing a line of code, accept the framing: this is a Symfony2 bundle, not a Laravel package. You will not composer require it into a Laravel app — its Composer constraints and bundle registration target a dead Symfony branch, and pulling it in risks conflicts with the modern Symfony components Laravel already depends on. "Leveraging" it as a Laravel developer means one of two things:

  1. You inherited a legacy Symfony2 app that already uses it, and need to maintain or migrate away from it.
  2. You want the min/max date-window semantics it demonstrates, ported into a first-party Laravel rule.

For case 2 — the common one — start by reading the source, not installing it. The only file that matters is the constraint validator in Validator/Constraints/ (look for the class extending Constraint and its paired ConstraintValidator). It is roughly 100 lines: a DateTime comparison against a min/max option. Read it, understand the boundary semantics (inclusive vs. exclusive), then move on. There is nothing else to learn from the package.

Minimal steps for the recommended path:

  1. Clone or browse the repo; read the constraint + validator classes.
  2. Decide whether Laravel's native rules already cover the requirement (they almost always do):
    $request->validate([
        'arrival_date' => ['required', 'date', 'after_or_equal:today', 'before_or_equal:'.now()->addYear()->toDateString()],
    ]);
    
  3. If you need a reusable, testable unit, create a rule in app/Rules/ (pattern below).
  4. First real use case: booking/reservation windows — "no past arrivals, no bookings more than a year out." That is the canonical acceptance criterion this bundle exists to satisfy, and it maps cleanly onto after_or_equal / before_or_equal.

If you're in case 1 (inheriting the Symfony app), the getting-started is: find the constraint usages via grep -r "OhDate" or the bundle's namespace, confirm they attach to entity/DTO date properties, and note the option values (they'll usually be relative strings like "today" or "+1 year" — see gotchas). That inventory becomes your port checklist when you migrate.

Implementation Patterns

Prefer native rule composition first. Laravel already ships the exact semantics. For a min/max window:

public function rules(): array
{
    return [
        'start_date' => ['required', 'date', 'after_or_equal:today'],
        'end_date'   => ['required', 'date', 'after_or_equal:start_date', 'before_or_equal:'.now()->addMonth()->toDateString()],
    ];
}

Note after_or_equal:start_date — cross-field comparison against another input is a native capability and one of the biggest reasons you don't need a custom package.

Custom Rule for the ported min/max logic. When the window is configured (not hardcoded) or needs reusable, tested behavior, port the bundle's comparison into an invokable rule:

namespace App\Rules;

use Carbon\Carbon;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class DateBetween implements ValidationRule
{
    public function __construct(
        private string $min,
        private string $max,
        private bool $inclusive = true,
    ) {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        try {
            $date = Carbon::parse($value);
        } catch (\Throwable) {
            $fail("The :attribute is not a valid date.");
            return;
        }

        $min = Carbon::parse($this->min);
        $max = Carbon::parse($this->max);

        $ok = $this->inclusive
            ? $date->betweenIncluded($min, $max)
            : $date->betweenExcluded($min, $max);

        if (! $ok) {
            $fail("The :attribute must be between {$min->toDateString()} and {$max->toDateString()}.");
        }
    }
}

Usage: 'arrival_date' => ['required', 'date', new DateBetween('today', '+1 year')]. The $inclusive flag is the one knob the bundle's boundary logic makes explicit — carry it over deliberately rather than guessing.

Relative reference dates. The bundle accepts strings like "today" and "+1 year". Carbon's parser accepts the same relative grammar, so new DateBetween('today', '+1 year') stays faithful to the original. But resolve relative dates at validation time, not at rule construction, so a long-running queue worker doesn't validate against yesterday's "today."

FormRequest integration. Put the composed rules in a FormRequest — this is Laravel's equivalent of the Symfony form-type pattern, and it keeps controllers clean:

class StoreBookingRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'arrival_date' => ['required', 'date', 'after_or_equal:today', 'before_or_equal:'.now()->addYear()->toDateString()],
        ];
    }
}

Testing the port. If you port the semantics, you own the test coverage — the bundle almost certainly doesn't have modern tests. Cover the boundaries explicitly: exact-min, exact-max, one-day-before, one-day-after, null, malformed string, and timezone edge cases. Freeze time with Carbon::setTestNow() so "today"-relative rules are deterministic:

Carbon::setTestNow('2024-06-15');
$this->post('/bookings', ['arrival_date' => '2024-06-15'])->assertSessionHasNoErrors();
$this->post('/bookings', ['arrival_date' => '2024-06-14'])->assertSessionHasErrors('arrival_date');

Gotchas and Tips

Do not add it to composer.json. This bears repeating because it's the single highest-risk mistake. A Symfony2 bundle drags Symfony2-era requirements that will either fail to resolve against Laravel's modern symfony/* components or force downgrades across your dependency tree. If you must inspect it at runtime for a legacy migration, do it in a throwaway container, not your app.

Inclusive vs. exclusive is where bugs hide. The bundle's value is precisely that it distinguishes "on the boundary" from "strictly inside." When you port, decide explicitly: after_or_equal:today accepts today; after:today does not. Getting this wrong is the classic off-by-one-day validation bug, and it surfaces as a customer complaint, not a test failure — unless you test the boundary.

date vs. date_format — be strict about input shape. date accepts a wide range of parseable strings and can surprise you (e.g., Carbon::parse interpreting ambiguous formats). If your form submits Y-m-d, add date_format:Y-m-d alongside date so the rule rejects ISO-8601 datetimes and other representations you didn't intend to accept. The bundle's format strictness is worth mirroring.

Timezone mismatches cause false rejections. now() uses config('app.timezone'), but a user-submitted date is timezone-naive. If the app timezone isn't UTC and you're comparing a bare Y-m-d against a now()-derived bound, you can reject "today" for users in a different timezone. Normalize: compare ->toDateString() values when the input is date-only, and document the timezone assumption. This is the failure mode most likely to escape to production.

Relative strings are not immutable in long-running processes. "+1 year" resolved once at boot in a queue worker means "one year from worker start," not "one year from now." Resolve relative bounds inside validate()/rules(), never in a constructor or property default.

Handle null/malformed defensively in your custom rule. The bundle's validator assumes a DateTime-ish value; your rule should catch parse exceptions and fail with a clear message rather than letting Carbon::parse throw a 500. Pair custom rules with nullable where the field is optional so empty input doesn't hit the parser at all.

Localize the message. The biggest practical win of porting over installing is that your rule returns a Laravel validation message you can put in lang/en/validation.php for translation. The bundle's hardcoded messages won't participate in Laravel's i18n — another reason to port.

Extension point: own the rule. The correct home for ported logic is a first-party class in app/Rules/ (or a small internal package), with tests committed alongside it. That gives you a clear owner, upgrade safety, and the ability to evolve semantics (business-day adjustments, holiday awareness) that the bundle never supported anyway. If you need genuine scheduling semantics — business calendars, time-of-day cutoffs, recurring windows — reach for a dedicated date/time library instead of extending any rule derived from this bundle; it's a date-boundary check, not a scheduling engine.

Treat it as reference material, not a dependency. With 4 stars, 0 dependents, and no maintenance signal, the durable artifact you keep is the idea — inclusive min/max date bounds — reimplemented in ~30 lines of Laravel-native code you fully control.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib