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.
Logo¶
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_ticketadds aticketssection to yoyaku_order'sOrderNotificationEvent, 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.