---
title: "Configuration — Opening Hours for Laravel | Roundly"
description: "Every config key, its default and env variable — timezones, search windows, limits, cache, API output, pruning and materialization."
url: https://roundly-consulting.com/open-source/docs/opening-hours-for-laravel/configuration
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Opening Hours for Laravel](https://roundly-consulting.com/open-source/docs/opening-hours-for-laravel.md)

# Configuration

The config is merged, so you override only what you need — and most knobs are env-backed. The published config/opening-hours.php:

```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 (isOpenDuring() and isClosedDuring() included), slot search or availability check may cover (1–3660); slots and checks also count a booking’s duration plus both buffers against it. 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 | — | Live named calendars per owner — checked when a sync creates a calendar or restores a soft-deleted one. |
| `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); the SQL scopes reach now + days\_ahead − 1 day. |
| `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:

```bash
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-hours
```

## php 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 crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## 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.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/opening-hours-for-laravel.md)
