Search by

glitchr / omnifood

GlitchArt

Omnifood: one contract for the restaurant platforms - delivery orders (Uber Eats, Deliveroo, Just Eat) and table reservations (TheFork, Zenchef) - their menus, their stores, their webhooks, and its Symfony bundle.

1.x-dev 2026-10-03 16:33 UTC

This package is auto-updated.

Last update: 2026-10-03 16:36:05 UTC


README

One contract for the restaurant platforms - the delivery orders (Uber Eats, Deliveroo, Just Eat) and the table reservations (TheFork, Zenchef) - the Omnibus of the kitchen, beside glitchr/omnitrade (payments), glitchr/omnibus (shipping) and glitchr/omnipost (publishing).

// A webhook: checked, read, the same shape whatever the platform.
$notification = $ubereats->notify($request->getContent(), $request->headers->all());
$order = $notification->order ?? $ubereats->order($notification->reference);

$ubereats->accept($order->reference, new \DateTimeImmutable('+20 minutes'));   // within 11.5 minutes
$ubereats->ready($order->reference);                                            // the courier may come

$deliveroo->pushMenu($menu);                       // checked first: InvalidMenuException, nothing sent
$deliveroo->setAvailability('gyoza', false);       // out of stock
$justeat->pause(new \DateTimeImmutable('+30 minutes'));

foreach ($thefork->reservations(new \DateTimeImmutable('today'), new \DateTimeImmutable('tomorrow')) as $reservation) {
    echo $reservation->date->format('H:i'), ' ', $reservation->covers, ' ', $reservation->guest->name(), "\n";
}

Capabilities, not one big interface. The platforms do different things: a platform implements what it offers - OrdersInterface, MenuInterface, StoreInterface, ReservationsInterface, NotifiableInterface - and Auth\RefreshableInterface / Auth\OAuthInterface for its tokens. What a platform offers but its public documentation does not show throws NotSupportedException: nothing is guessed.

One model. Order (its Lines and their Modifiers, Customer, Courier, Money in minor units, OrderType, OrderStatus), Menu\{Menu, Category, Item, ModifierGroup, Modifier} (prices, VAT rates, the EU-14 Allergens, the restaurant's own references), Reservation (Guest, ReservationStatus), Slot, Hours, StoreStatus, Notification, Token. Each keeps the platform's own answer in $raw.

Checked before it is sent. capabilities() says what a platform takes (its order types, how long it gives to accept, how deep modifiers nest, allergens, photos, VAT rates, names' lengths...); the Validator lists what a menu breaks, by path, for a back office to show; pushMenu() runs it first.

The keys stay with the restaurant. A platform is built from options - a client id, a secret, a store id - and calls the platform directly through the application's HTTP client. Omnifood depends on nothing but symfony/http-client-contracts; it never depends on omnibase.

Package Platform Capabilities
omnifood/ubereats Uber Eats (Marketplace APIs) orders, menu, store, webhooks, token, OAuth (store linking)
omnifood/deliveroo Deliveroo (Partner Platform) orders, menu, store, webhooks, token
omnifood/justeat Just Eat Takeaway.com (JET Connect) orders (pushed), menu, store, webhooks
omnifood/thefork TheFork (B2B API) reservations, webhooks, token
omnifood/zenchef Zenchef credentials only: the API is documented on request

None of these platforms gives credentials without a partner agreement: every package is written from the platform's public documentation and tested against recorded answers (MockHttpClient), not verified against the live APIs. Each README says what to ask for.

Install

composer require glitchr/omnifood omnifood/ubereats omnifood/deliveroo omnifood/justeat omnifood/thefork

Symfony

Omnifood\Bridge\Symfony\OmnifoodBundle: every omnifood/* package installed registered, Omnifood\Registry and Omnifood\Validator autowired, each configured platform injectable by its name, as what it does.

omnifood:
    platforms:
        ubereats:  { factory: ubereats, options: { client_id: '%env(default::UBEREATS_CLIENT_ID)%', client_secret: '%env(default::UBEREATS_CLIENT_SECRET)%', store_id: '%env(default::UBEREATS_STORE_ID)%' } }
        deliveroo: { factory: deliveroo, options: { client_id: '%env(default::DELIVEROO_CLIENT_ID)%', client_secret: '%env(default::DELIVEROO_CLIENT_SECRET)%', brand_id: '%env(default::DELIVEROO_BRAND_ID)%', site_id: '%env(default::DELIVEROO_SITE_ID)%', menu_id: '%env(default::DELIVEROO_MENU_ID)%', webhook_secret: '%env(default::DELIVEROO_WEBHOOK_SECRET)%' } }
        thefork:   { factory: thefork, options: { client_id: '%env(default::THEFORK_CLIENT_ID)%', client_secret: '%env(default::THEFORK_CLIENT_SECRET)%', restaurant_id: '%env(default::THEFORK_RESTAURANT_ID)%', webhook_token: '%env(default::THEFORK_WEBHOOK_TOKEN)%' } }
public function __construct(OrdersInterface $ubereats, MenuInterface $deliveroo, ReservationsInterface $thefork, Registry $omnifood, Validator $validator) {}

Nothing is built when the container compiles: a platform is built the first time it is asked for, and a key left empty only shows when a call needs it (InvalidConfigException) - capabilities() needs none. Keys typed in a back office rather than kept in the environment: $registry->create('ubereats', ['client_id' => ..., 'client_secret' => ...]). An application's own PlatformFactoryInterface is registered too (autoconfigured).

Without Symfony

$registry = new Registry([new UberEatsPlatformFactory($http), new TheForkPlatformFactory($http)], [
    'ubereats' => ['factory' => 'ubereats', 'options' => ['client_id' => '...', 'client_secret' => '...', 'store_id' => '...']],
    'thefork' => ['factory' => 'thefork', 'options' => ['client_id' => '...', 'client_secret' => '...', 'restaurant_id' => '...']],
]);
$registry->orders();        // the platforms that take orders
$registry->reservations();  // the ones that take reservations

Docker: every platform with your keys

docker/ runs this package with every omnifood/* platform - from GitHub, or from the checkouts beside this one when OMNIFOOD_PLUGINS=../.. is set - and a console that exercises them with the keys in docker/.env (copy .env.dist):

cd docker && cp .env.dist .env
docker compose run --rm omnifood platforms                       # installed, configured, what each does
docker compose run --rm omnifood capabilities ubereats           # no key needed
docker compose run --rm omnifood menu:push deliveroo /omnifood/core/docker/menu.example.json --validate
docker compose run --rm omnifood orders deliveroo --since "-2 hours"
docker compose run --rm omnifood order ubereats <id>
docker compose run --rm omnifood accept ubereats <id> --ready-at "+20 minutes"
docker compose run --rm omnifood deny ubereats <id> --reason too_busy
docker compose run --rm omnifood ready deliveroo <id>
docker compose run --rm omnifood store:status ubereats
docker compose run --rm omnifood reservations thefork --from today --to "+7 days"
docker compose run --rm omnifood notify ubereats /omnifood/core/docker/body.json -H "X-Uber-Signature: ..."
docker compose run --rm omnifood refresh thefork
docker compose run --rm omnifood test                            # every package's tests

More in docs/.

License: LGPL-3.0-or-later.