Configuration
The config is merged, so you override only what you need — and most knobs are env-backed. The published config/opening-hours.php:
use RoundlyConsulting\OpeningHours\Models\Calendar;
use RoundlyConsulting\OpeningHours\Models\ExceptionRule;
use RoundlyConsulting\OpeningHours\Models\Schedule;
return [
'key_type' => env('OPENING_HOURS_KEY_TYPE', 'bigint'),
'models' => [
'calendar' => Calendar::class,
'schedule' => Schedule::class,
'exception_rule' => ExceptionRule::class,
],
'default_calendar' => env('OPENING_HOURS_DEFAULT_CALENDAR', 'default'),
'timezone' => env('OPENING_HOURS_TIMEZONE'),
'first_day_of_week' => env('OPENING_HOURS_FIRST_DAY_OF_WEEK', 'monday'),
'search_days' => env('OPENING_HOURS_SEARCH_DAYS', 366),
'max_query_days' => env('OPENING_HOURS_MAX_QUERY_DAYS', 366),
'delete_with_owner' => env('OPENING_HOURS_DELETE_WITH_OWNER', true),
'limits' => [
'calendars' => 16, 'schedules' => 20, 'ranges_per_day' => 12, 'exceptions' => 1000,
'label_length' => 191, 'meta_bytes' => 4096, 'busy_periods' => 10000, 'slots' => 2000,
],
'cache' => [
'enabled' => env('OPENING_HOURS_CACHE_ENABLED', true),
'store' => env('OPENING_HOURS_CACHE_STORE'),
'ttl' => env('OPENING_HOURS_CACHE_TTL', 86400),
'prefix' => env('OPENING_HOURS_CACHE_PREFIX', 'opening-hours'),
],
'api' => ['expose_meta' => false, 'week_mode' => 'upcoming', 'upcoming_exceptions_days' => 60],
'prune' => [
'exceptions_after_days' => env('OPENING_HOURS_PRUNE_EXCEPTIONS_AFTER_DAYS'),
'trashed_after_days' => env('OPENING_HOURS_PRUNE_TRASHED_AFTER_DAYS'),
],
'materialize' => [
'enabled' => env('OPENING_HOURS_MATERIALIZE', false),
'days_ahead' => env('OPENING_HOURS_MATERIALIZE_DAYS_AHEAD', 60),
'days_behind' => env('OPENING_HOURS_MATERIALIZE_DAYS_BEHIND', 1),
'connection' => env('OPENING_HOURS_QUEUE_CONNECTION'),
'queue' => env('OPENING_HOURS_QUEUE'),
],
'facade_alias' => 'OpeningHours',
];Every key
| Key | Default | Env | Purpose |
|---|---|---|---|
key_type | bigint | OPENING_HOURS_KEY_TYPE | Owner primary-key type (bigint, uuid, ulid); sets the owner_id column type in the migrations. Any other value throws. |
models.calendar | Calendar::class | — | Calendar model; swap in a subclass (anything else throws). |
models.schedule | Schedule::class | — | Schedule model; swap in a subclass (anything else throws). |
models.exception_rule | ExceptionRule::class | — | Exception model; swap in a subclass (anything else throws). |
default_calendar | default | OPENING_HOURS_DEFAULT_CALENDAR | Calendar key used when none is given. |
timezone | null | OPENING_HOURS_TIMEZONE | Fallback timezone before app.timezone (IANA names only; not set — null or blank — = app.timezone). |
first_day_of_week | monday | OPENING_HOURS_FIRST_DAY_OF_WEEK | Ordering of forWeek(), forWeekOf() and the calendar-week API mode. |
search_days | 366 | OPENING_HOURS_SEARCH_DAYS | How many local days after/before the given day next*/previous* and nextAvailableSlot() look (1–3660); beyond it they return null. |
max_query_days | 366 | OPENING_HOURS_MAX_QUERY_DAYS | Largest span a span query or slot search may cover (1–3660); nextAvailableSlot() and the status resource stay within it on their own. |
delete_with_owner | true | OPENING_HOURS_DELETE_WITH_OWNER | Remove calendars when their owner is deleted permanently. |
limits.calendars | 16 | — | Named calendars per owner. |
limits.schedules | 20 | — | Schedules per calendar. |
limits.ranges_per_day | 12 | — | Ranges per weekday / per exception. |
limits.exceptions | 1000 | — | Exceptions per calendar. |
limits.label_length | 191 | — | Maximum label length. |
limits.meta_bytes | 4096 | — | Maximum JSON size of a meta payload. |
limits.busy_periods | 10000 | — | Busy periods read per availability evaluation. |
limits.slots | 2000 | — | Maximum slots per slot query. |
cache.enabled | true | OPENING_HOURS_CACHE_ENABLED | Cache compiled definitions. |
cache.store | null | OPENING_HOURS_CACHE_STORE | Cache store (not set — null or blank — = default store; a set value must be a string). |
cache.ttl | 86400 | OPENING_HOURS_CACHE_TTL | Seconds; null = forever (a blank env value is not set, so 86400). |
cache.prefix | opening-hours | OPENING_HOURS_CACHE_PREFIX | Cache key prefix (a string; blank = not set = opening-hours). |
api.expose_meta | false | — | Include meta in API resources. |
api.week_mode | upcoming | — | Status resource week: upcoming (next 7 dates) or calendar_week. |
api.upcoming_exceptions_days | 60 | — | Look-ahead of the status resource (capped at max_query_days − 1) and structured data. |
prune.exceptions_after_days | null | OPENING_HOURS_PRUNE_EXCEPTIONS_AFTER_DAYS | Prune one-off exceptions that ended N days ago (not set — null or blank — = off). |
prune.trashed_after_days | null | OPENING_HOURS_PRUNE_TRASHED_AFTER_DAYS | Purge soft-deleted rows older than N days (not set — null or blank — = off). |
materialize.enabled | false | OPENING_HOURS_MATERIALIZE | Maintain the opening_hours_intervals table for SQL scopes. |
materialize.days_ahead | 60 | OPENING_HOURS_MATERIALIZE_DAYS_AHEAD | Materialized horizon forward (days). |
materialize.days_behind | 1 | OPENING_HOURS_MATERIALIZE_DAYS_BEHIND | Materialized horizon backward (days). |
materialize.connection | null | OPENING_HOURS_QUEUE_CONNECTION | Queue connection of the materialization job (not set — null or blank — = default; else a string). |
materialize.queue | null | OPENING_HOURS_QUEUE | Queue of the materialization job (not set — null or blank — = default; else a string). |
facade_alias | OpeningHours | — | Global facade alias; null or a false spelling (false, 0, off, no) disables it; blank is not set and keeps OpeningHours. |
Env values arrive as strings and are validated when read: the switches (delete_with_owner, cache.enabled, materialize.enabled, api.expose_meta) take true/1/on/yes or false/0/off/no, the numeric keys take whole numbers within their range, and key_type, first_day_of_week and api.week_mode take one of their listed values. A blank value ('' or whitespace, as a bare OPENING_HOURS_SEARCH_DAYS= line gives) is not set and takes the default in the table. Anything else — a typo such as OPENING_HOURS_CACHE_ENABLED=disabled or OPENING_HOURS_SEARCH_DAYS=abc, a non-string cache store, prefix, queue or connection, or a timezone that isn’t a string — throws the toolkit’s InvalidConfigurationException instead of quietly becoming a default.
null keeps its documented meaning: the default store, connection or queue, app.timezone for timezone, forever for cache.ttl, off for prune.*, and no alias for facade_alias. A blank value means the same as null, except for cache.ttl, where it keeps 86400, and facade_alias, where it keeps OpeningHours.
Environment
Every env-backed key at a glance:
OPENING_HOURS_KEY_TYPE=bigint
OPENING_HOURS_DEFAULT_CALENDAR=default
OPENING_HOURS_TIMEZONE=Europe/Bratislava
OPENING_HOURS_FIRST_DAY_OF_WEEK=monday
OPENING_HOURS_SEARCH_DAYS=366
OPENING_HOURS_MAX_QUERY_DAYS=366
OPENING_HOURS_DELETE_WITH_OWNER=true
OPENING_HOURS_CACHE_ENABLED=true
OPENING_HOURS_CACHE_STORE=redis
OPENING_HOURS_CACHE_TTL=86400
OPENING_HOURS_CACHE_PREFIX=opening-hours
OPENING_HOURS_PRUNE_EXCEPTIONS_AFTER_DAYS=30
OPENING_HOURS_PRUNE_TRASHED_AFTER_DAYS=90
OPENING_HOURS_MATERIALIZE=true
OPENING_HOURS_MATERIALIZE_DAYS_AHEAD=60
OPENING_HOURS_MATERIALIZE_DAYS_BEHIND=1
OPENING_HOURS_QUEUE_CONNECTION=redis
OPENING_HOURS_QUEUE=opening-hoursphp artisan about
The package adds an Opening-hours section to php artisan about: the three model classes, key type, default calendar and timezone, search days, cache on/off, the cache store (shown only as default or custom), materialization on/off and the facade alias. A malformed setting other than a model class shows as INVALID, so the command still renders on a misconfigured host.
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.