Skip to content

Tickets

The yoyaku_ticket submodule turns a confirmed order into printable or emailed tickets: a PDF carrying a QR code that identifies the booking and can be scanned at the door. Like payment, ticketing is a submodule rather than part of the core engine, so a site enables it only when it needs tickets.

What a ticket is

Core produces one ticket per confirmed booking. A booking is one unit on a slot, so the ticket admits the booking's quantity and its QR names that booking. There is no per-unit option in core: without numbered placement there is nothing finer than the booking to ticket, so there is nothing to configure. The placement bridge (see below) adds per-place tickets where they make sense.

Each ticket shows the resource, the slot time, how many it admits, the booker and the order number, and the QR code.

Which resources issue tickets

Ticketing is opt-in per resource: a confirmed booking gets a ticket only when its resource has ticketing on. The resource's own Ticketing field answers it (inherit, on, or off), and a resource saying it inherits is answered by whichever level rules the field through its field rules. Where nothing says on, ticketing is off, so a plain reservation never gets a QR code by accident. The same gate governs the confirmation email: with ticketing off, the order's notification carries no ticket attachment.

The QR token

The QR encodes a signed, self-verifying token, not a database id. The token binds the reference kind (booking or order) and the entity UUID into an HMAC signed with the site private key and hash salt, so a door scanner can validate it offline and it cannot be forged. The token expires at the slot end plus a short grace period, so a ticket scans right up to (and a little past) the slot it admits to, and a leaked QR is dead once the slot is over.

The attendance check-in submodule reads this token to complete a booking when the ticket is scanned.

PDF rendering

Rendering is behind a plugin, so the engine that draws the PDF is swappable. The bundled Dompdf renderer (dompdf/dompdf) is pure PHP and needs no external service. A site with a different need adds its own PdfRenderer plugin (for example a Gotenberg service) and selects it in the ticket settings, with no change to the builder. The active renderer is set in yoyaku_ticket.settings.

The printed page

What the renderer draws is a themed document, not markup assembled in PHP. Two templates split the work:

Template Theme hook Owns
yoyaku-ticket.html.twig yoyaku_ticket One ticket: its rows, logo and QR code.
yoyaku-ticket-document.html.twig yoyaku_ticket_document The page around them: the document, the print styles, and the page break between tickets.

So a site restyles the printed page (margins through an @page rule, a footer, its own print CSS) by overriding the document template, and restyles the ticket itself without touching the page. The builder renders the document once and hands the HTML to the renderer plugin.

The document template carries its styles inline, in a <style> element, because a PDF renderer draws the HTML on its own and never fetches the site's stylesheets: an attached library would simply be dropped. The summary's mail template carries its styles the same way, for the same reason.

A ticket carries a logo to brand it. Upload one at Administration › Booking › Ticket settings for the whole site, and a tenant can override it on its own edit form for its tickets. The logo cascades from the most specific level to the least: the booking's tenant, then the site-wide setting, then the site theme's own logo, so a single-tenant site needs no configuration and still shows its branding, while a tenant can brand its own tickets.

The chosen image is inlined into the PDF as a data URI, because the PDF has no access to the running site. A dark, print-friendly image reads best on the white ticket (the site theme logo is often light, designed for a dark header). Uploaded logos are tracked through file usage, so replacing or clearing one releases the previous file to be purged rather than left on disk. The logo is a plain logo variable in the yoyaku-ticket template: restyle it with the .ticket__logo class or override the template to place it wherever the design needs.

Delivery

The booker gets their tickets three ways, from the same signed token:

  • In the confirmation email. yoyaku_ticket adds a tickets section to yoyaku_order's OrderNotificationEvent, which the shipped confirmation templates place with [yoyaku-notification:section:tickets]. It lists every ticket (what it admits to, when, and its place with the placement bridge), shows each one's QR code in the body, and links the ticket page. Each ticket's PDF is attached too. The event is dispatched in the order's booking channel, so every link and PDF carries that front's address and branding.
  • On the ticket page. /booking/tickets/{token} shows every ticket as its PDF prints it, QR code included, with a link to that ticket's PDF (/booking/ticket/{token}/{number}) and one to all of them in one PDF. A ticket's PDF is rendered on its first download and kept in the cache until the order, its bookings, their slots or resources, the tenant or the ticket settings change.
  • By the download link. The [yoyaku-order:ticket-url] token streams the order's tickets as one PDF, for a template or a confirmation screen of a site's own.

The link and the page are public routes: the signed token is the capability, and it expires six hours after the order's last slot ends.

How much the email carries

Every QR code and PDF is built while the email is, so a large order would make a slow confirmation and a heavy email. Three settings, under Administration › Booking › Ticket settings › Delivery by email, bound them by the order's number of tickets:

  • Show the QR codes in the email up to (50 by default): above it, the email lists the tickets without their codes.
  • Attach the ticket PDFs up to (10 by default): above it, nothing is attached.
  • Link the email to the ticket page (on by default). The page is linked anyway when the email carries neither the codes nor the PDFs, since it is then the booker's only way to them.

Mail systems

A QR code in the body is an image sent with the email and referenced by cid: its file name (ticket-<order number>-<n>-qr.png). Symfony Mailer shows it. Mime Mail attaches it as a separate file and the body's image breaks, until #3626119 is fixed; the PDFs and the page link work with either.

Theming

The section is two templates, table markup with inline styles because mail clients ignore stylesheets: yoyaku-ticket-email-list around the rows, and yoyaku-ticket-email-item for one ticket. The ticket page is yoyaku-ticket-page, which prints each ticket through yoyaku-ticket, the template the PDF prints. A one-ticket template takes suggestions per tenant, per resource type and per resource; a template of several tickets takes the tenant one only. The section's own words (the labels and the page link) are in the booker's language.

Requirements

Install the PDF and QR libraries the submodule needs:

composer require dompdf/dompdf endroid/qr-code

Placement

With the yoyaku_ticket_placement bridge, a ticket for a placed booking names where to attend: its venue on a Where line, and its section and seat (for example "Parterre - L15") on a Place line. No granularity choice is needed, because a placed resource already makes one booking per seat, so the one-ticket-per-booking rule is one ticket per seat. The bridge only names each ticket: it never splits or drops one, and the QR still identifies the same booking. A booking with no place (an unplaced resource in the same order) tickets unchanged.

It works through the hook_yoyaku_tickets_alter seam, so the ticket builder stays unaware of placement, and placement never depends on ticketing.