---
title: "The OpeningHours facade — Opening Hours for Laravel | Roundly"
description: "Every write and read through one facade — sync, edit, exceptions(), delete(force:), refresh, validate — and the query object it returns."
url: https://roundly-consulting.com/open-source/docs/opening-hours-for-laravel/facade
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)

# The OpeningHours facade

Everything goes through the OpeningHours facade over RoundlyConsulting\\OpeningHours\\OpeningHoursManager — the recommended way in. The manager is bound scoped (one memo per request or job) and aliased globally as OpeningHours (config facade\_alias). The owner trait’s methods are shortcuts to the same calls — $clinic->setOpeningHours() is OpeningHours::sync($clinic):

```php
use RoundlyConsulting\OpeningHours\DataTransferObjects\ExceptionData;
use RoundlyConsulting\OpeningHours\Facades\OpeningHours;

OpeningHours::sync($clinic, ['week' => ['monday' => ['09:00-17:00']]]); // replace the whole definition
OpeningHours::for($clinic)->isOpen();                                   // the query object
OpeningHours::edit($clinic)->closed('2026-12-24')->save();              // builder, optimistic save

// One exception at a time — locked, validated against the whole definition, never stale:
OpeningHours::exceptions($clinic)->closed('2026-12-24', label: 'Christmas Eve');
OpeningHours::exceptions($clinic)->open('2026-12-31', ['09:00-13:00']);
OpeningHours::exceptions($clinic, 'pickup')->add(ExceptionData::make('12-26', label: 'St Stephen'));
OpeningHours::exceptions($clinic)->all();                               // list<ExceptionData>, with ids
OpeningHours::exceptions($clinic)->remove($ruleId);                     // false for another calendar's rule

OpeningHours::delete($clinic);                                          // soft; the next sync restores it
OpeningHours::delete($clinic, 'pickup', force: true);                   // gone for good
```

Exception writes throw CalendarNotFoundException when the owner has no live calendar under the key — create it with sync() first. delete() is soft by default, so the next sync restores the calendar; force: true removes it for good, a soft-deleted one included.

## Every method

| Method | Purpose |
| --- | --- |
| `for($owner, ?$calendar)` | The query object — same as $owner->openingHours(). |
| `calendar($owner, ?$calendar)` | The live ?Calendar header (null for an unsaved owner). |
| `has($owner, ?$calendar)` | Whether the calendar exists. |
| `edit($owner, ?$calendar)` | A CalendarBuilder seeded with the current definition. |
| `sync($owner, $data, ?$calendar, ?$expectedRevision)` | Full replace; array input is parsed and validated first. |
| `make($data, ?$timezone)` | An in-memory OpeningHours — no database. |
| `exceptions($owner, ?$calendar)` | A CalendarExceptions handle — closed(), open(), add(), all(), remove() — for race-safe single-exception writes. |
| `delete($owner, ?$calendar, force: false)` | Soft delete by default (the next sync restores it); force: true removes it for good, a soft-deleted one included; false when absent. |
| `refresh($owner, ?$calendar)` | Bump the revision after raw SQL or a changed timezone hook. |
| `validate($payload, ?$options)` | A ViolationList — nothing is written. |
| `definitionData($calendar)` | The stored definition of a Calendar header as CalendarData, through memo and cache. |
| `flushMemo()` | Clear the per-request memo. |
| `fake()` | Swap in the recording OpeningHoursFake — see Testing. |

## Two classes named OpeningHours

The query object shares the facade’s short name — alias one where a file needs both:

```php
use RoundlyConsulting\OpeningHours\Facades\OpeningHours as Hours;
use RoundlyConsulting\OpeningHours\OpeningHours;

Hours::for($clinic, 'pickup')->forWeek()->grouped();
Hours::has($clinic, 'pickup');
Hours::validate($request->input('hours', []))->toMessageBag('hours');

$preview = OpeningHours::make(['week' => ['saturday' => ['22:00-03:00']]], 'Europe/Bratislava');
```

Want the same API without static calls? See DI and actions.

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