Validation and HTTP input
Validate form or API input with the ValidOpeningHours rule, then pass the validated array straight to OpeningHours::sync():
use RoundlyConsulting\OpeningHours\Facades\OpeningHours;
use RoundlyConsulting\OpeningHours\Rules\ValidOpeningHours;
use RoundlyConsulting\OpeningHours\Rules\ValidTimeRange;
public function rules(): array
{
return [
'opening_hours' => ['required', 'array', new ValidOpeningHours(requireTimezone: true)],
'range' => [new ValidTimeRange],
];
}
OpeningHours::sync(
$clinic,
$request->validated('opening_hours'),
expectedRevision: $request->filled('revision') ? $request->integer('revision') : null,
);Every problem is reported under its nested key (opening_hours.schedules.0.week.monday.1) with a translated message: invalid times, empty ranges, overlaps (also across midnight and from Sunday into Monday), two base schedules, ambiguous seasonal windows or exceptions, unknown weekdays, invalid timezones and exceeded limits. new ValidOpeningHours(mergeOverlapping: true) merges overlaps instead of rejecting them; requireTimezone: true makes the timezone mandatory. ValidTimeRange validates a single range.
Programmatic validation
The facade validates without writing; CalendarData::fromArray() throws InvalidOpeningHoursException carrying every violation:
use RoundlyConsulting\OpeningHours\DataTransferObjects\CalendarData;
use RoundlyConsulting\OpeningHours\Exceptions\InvalidOpeningHoursException;
use RoundlyConsulting\OpeningHours\Facades\OpeningHours;
$violations = OpeningHours::validate($payload); // ViolationList, nothing is written
$violations->isEmpty();
$violations->first()?->message();
$violations->toMessageBag('opening_hours');
try {
$data = CalendarData::fromArray($payload);
} catch (InvalidOpeningHoursException $e) {
return back()->withErrors($e->violations()->toMessageBag('opening_hours'));
}Violation codes
Each violation carries a ViolationCode; its message comes from opening-hours::validation.<code>, in English and Slovak: invalid_structure, invalid_time, empty_range, start_at_24, overlap, invalid_date, invalid_month_day, window_inverted, recurrence_mismatch, yearly_window_unbounded, duplicate_base_schedule, ambiguous_schedule_window, duplicate_exception, ambiguous_exception, invalid_timezone, timezone_required, invalid_calendar_key, unknown_weekday, invalid_priority, invalid_capacity, limit_exceeded, label_too_long, meta_too_large, unknown_id, duplicate_id and unsupported_week_array_feature.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.