- Issue created by @martin@manarock.co.uk
- 🇬🇧United Kingdom martin@manarock.co.uk
Is anyone able to suggest a route forward ?
I still need to be able to allow a user to add values to a ticket on purchase.
I have tried to to use ECA but don't seem able to trigger a response to the checkout event at pre-save.Would https://www.drupal.org/project/field_inheritance → be a plausible way inheriting the details from the product onto the ticket?
- 🇬🇧United Kingdom martin@manarock.co.uk
So I have eventually managed to get this working. Going to dump a number of things here in the hope that it helps someone and hopefully becomes somewhat of a reference. Am sure there would have been easier/better ways, but I am far from a developer!
Firstly the end result:
To get a working D10 install:
Follow the instructions here : https://www.drupal.org/project/commerce_ticketing/issues/3382034#comment... ✨ Drupal 10 upgrade RTBC (note the repo comments that follow)
In addition, I have these patches:"drupal/entity_print": { "3383187 - Unexpected Error, Session has not been set": "https://www.drupal.org/files/issues/2023-12-06/3383187-5.patch" }, "drupal/commerce_ticketing": { "3416185 - symphony mailer plugin": "https://git.drupalcode.org/project/commerce_ticketing/-/merge_requests/29.diff", "3415783 - tickets not autosending": "https://www.drupal.org/files/issues/2024-03-13/autoactivating-3415783-12.patch", "3425570 - Error: Call to a member function getWorkflowId() on null": "https://www.drupal.org/files/issues/2024-03-05/commerce_ticketing-3425570.patch", "3250757 - Events and Disabled Tickets": "https://www.drupal.org/files/issues/2024-03-12/events-disabled-tickets-3250757-8.patch", "3415300 - ticketing twig theming patch": "https://www.drupal.org/files/issues/2024-01-17/theming-3415300-4.patch"
Rather than store additional details in a field on the ticket entity (although I tried!) I ended up using a pre-process function in my own module to make the values I needed available on screen, and when printing.
For me they are:
- details from the product (that sold the ticket)
- details from the event that the product was related to (I am using https://www.drupal.org/project/recurring_events → for events), all products based on ticket type 'event ticket' have required field, that is an entity reference to an event instance.
- details from the account / user that transacted the ticket.In my module I implement:
/** * Implements hook_preprocess_HOOK() for commerce_ticket templates. * * Enhances the ticket template with detailed information including: * - Ticket specifics such as type and associated product details. * - Ownership details based on ticket assignments. * - Customer information derived from the order, prioritizing billing profile data with fallbacks to user data. * - Event details linked through the product variation. * * @param array &$variables * The variables array passed to the ticket template. */ function geminigames_tickets_mod_preprocess_commerce_ticket(&$variables) { // Extract the ticket entity from the render array. $ticket = $variables['elements']['#commerce_ticket']; if ($ticket) { // Pass the ticket entity itself to the Twig template for debugging or other uses. $variables['ticket'] = $ticket; // Extract and pass the machine name of the ticket type to the template. $variables['ticket_type'] = $ticket->bundle(); // Extract and pass the plaintext version of the ticket number to the template. // Check if the ticket has a 'ticket_number' field and it has a value. if ($ticket->hasField('ticket_number') && !$ticket->get('ticket_number')->isEmpty()) { // Extract the ticket number value. $variables['plain_ticket_number'] = $ticket->get('ticket_number')->value; } // Extract and pass the ticket status to the template. // Check if the ticket has a 'state' field and it has a value. if ($ticket->hasField('state') && !$ticket->get('state')->isEmpty()) { // Extract the ticket status value. $variables['ticket_status'] = $ticket->get('state')->value; } if (isset($variables['content']['entity_print_view_pdf'])) { // Change the link text. $variables['content']['entity_print_view_pdf']['#title'] = t('here'); } } // Extract associated product details from the ticket's order item. if ($order_item = $ticket->getOrderItem()) { $product_variation = $order_item->getPurchasedEntity(); if ($product_variation) { $product = $product_variation->getProduct(); // Associated product entity. $variables['product_title'] = $product->getTitle(); $variables['product_variation_title'] = $product_variation->getTitle(); } } // Set details for the ticket's owner. $owner = $ticket->getOwner(); if ($owner) { $variables['ticket_owner_name'] = $owner->getDisplayName(); } // Extract and process customer information from the associated order. $order = $ticket->getOrder(); if ($order) { $customer_user = $order->getCustomer(); // User entity associated with the order. // Set customer's email from the user entity associated with the order. $variables['customer_email'] = $customer_user->getEmail(); // Initialize customer information variables. $customer_name = $customer_first_name = $customer_last_name = t('Not available'); // Attempt to extract name information from the billing profile's address field. $billing_profile = $order->getBillingProfile(); if ($billing_profile && $billing_profile->hasField('address')) { $address = $billing_profile->get('address')->first(); if ($address) { $customer_first_name = $address->getGivenName(); $customer_last_name = $address->getFamilyName(); $customer_name = $customer_first_name . ' ' . $customer_last_name; } } // Use user entity as fallback for names if not populated from billing profile. if ($customer_first_name == t('Not available') || $customer_last_name == t('Not available')) { $customer_name = $customer_user->getDisplayName(); } // Set finalized customer details in variables. $variables['customer_name'] = $customer_name; $variables['customer_first_name'] = $customer_first_name; $variables['customer_last_name'] = $customer_last_name; } // Retrieve and set event details linked to the product variation. if (isset($product_variation) && $product_variation->hasField('field_associated_event') && !$product_variation->get('field_associated_event')->isEmpty()) { $event = $product_variation->get('field_associated_event')->entity; if ($event) { // Assuming 'date' is a Daterange field containing event start and end times. $start_date = $event->get('date')->start_date; $end_date = $event->get('date')->end_date; // Set event details for the template. $variables['event_title'] = $event->label(); $variables['event_start_date'] = $start_date->format('Y-m-d'); $variables['event_start_time'] = $start_date->format('H:i'); $variables['event_end_date'] = $end_date->format('Y-m-d'); $variables['event_end_time'] = $end_date->format('H:i'); } } // Retrieve the active theme's settings. $theme = \Drupal::theme()->getActiveTheme()->getName(); $theme_settings = \Drupal::config('system.theme')->get('default'); $logo_path = theme_get_setting('logo.url', $theme); // Check if a custom logo has been set for the theme. if (!empty($logo_path)) { // Pass the logo URL to the template. $variables['theme_logo'] = $logo_path; } }
My twig file, then looks like this:
<div class="container mt-4"> <div class="row"> <div class="col"> <div class="alert alert-info" role="alert"> <h4 class="alert-heading">Ticket Information</h4> {% if event_title %}<p>Please find on this page your ticket for <strong> {{ event_title }}</strong> on <strong> {{ event_start_date }}</strong>.{% endif %} {% if not event_title %}<p>Please find on this page your ticket for <strong> {{ product_title }}</strong>.{% endif %} Please check your details and get in touch with Gemini Games if this information appears incorrect.</p> <p class="muted">{{ 'This ticket @ticket_number, was generated by order #@number.'|t({'@ticket_number': commerce_ticket.getTicketNumber, '@number': order.getOrderNumber}) }}</p> <hr> <p class="mb-0">- The status of your ticket is: <strong> {{ ticket_status }}</strong></p> <p class="mb-0">- Your ticket number is: <strong> {{ plain_ticket_number }}</strong></p> {% if content.entity_print_view_pdf %} <p class="mb-0 d-inline"> - You can download your PDF of this ticket <a href="{{ content.entity_print_view_pdf['#url'] }}" class="alert-link">here</a>. </p> {% endif %} </div> </div> </div> </div> <div class="row justify-content-center"> <div class="col-lg-8 col-md-10"> <div class="card mb-3 border border-4 border-dark"> <div class="row g-0"> <!-- Left Column: QR Code and Logo --> <div class="col-md-5 col-lg-4 bg-primary p-3 d-flex flex-column align-items-center justify-content-center"> <div class="qr-container bg-white p-1 mb-3" style="border: 4px solid black; max-width: 220px;"> {% if content.qr_code %} <div class="ticket-qr-code" style="width: 100%; height: auto;">{{ content.qr_code }}</div> {% endif %} </div> <!-- Theme Logo below QR Code --> {% if theme_logo %} <img src="{{ theme_logo }}" class="img-fluid mb-3" alt="Theme Logo"> {% endif %} </div> <!-- Right Column: Product, Ticket and Event Details --> <div class="col-md-7 col-lg-8 bg-light p-4"> <div class="card-body"> <!-- Product Title --> <div class="row mb-4"> <div class="col"> <h4 class="display-6">{{ product_title }}</h4> <!-- Larger title font --> </div> </div> <!-- Ticket Details --> <div class="row mb-4"> <div class="col"> <h5 class="card-title fw-bold">Ticket Details</h5> <!-- Ticket details heading --> <p class="card-text"> <strong>Ticket Type:</strong> {{ product_variation_title }}<br> <strong>Ticket Number:</strong> {{ plain_ticket_number }}<br> <strong>Customer Name:</strong> {{ customer_name }}<br> <strong>Customer Email:</strong> {{ customer_email }} </p> </div> </div> <!-- Event Details, displayed only if there is an event title --> {% if event_title %} <div class="row"> <div class="col"> <h5 class="card-title fw-bold">Event Details</h5> <!-- Event details heading --> <p class="card-text"> <strong>Event Title:</strong> {{ event_title }}<br> <strong>Event Date:</strong> {{ event_start_date }}<br> <strong>Event Time:</strong> {{ event_start_time }} </p> </div> </div> {% endif %} </div> </div> </div> </div> </div> </div>
Hopefully there is enough here to help someone!
I don't think any of this is contributable / patchable without a D10 release ?
- 🇬🇧United Kingdom martin@manarock.co.uk
Id love to know some next steps to progress - I need to put this into production fully, but am getting composer issues with:
mizpah@Asborian:~/development/gemini-gaming-production$ composer status -v > command: Drupal\Composer\Plugin\Scaffold\Plugin->onCommand You have changes in the following dependencies: web/modules/contrib/commerce_ticketing: M commerce_ticketing.install M commerce_ticketing.module M config/install/commerce_product.commerce_product_variation_type.ticket.yml M config/schema/commerce_ticketing.schema.yml M src/CommerceTicketCreateTickets.php M src/CommerceTicketListBuilder.php M src/CommerceTicketRouteProvider.php M src/Entity/CommerceTicket.php M src/EventSubscriber/OrderEventSubscriber.php M tests/src/Kernel/TicketCreationTest.php M tests/src/Kernel/TicketKernelTestBase.php
I should note that everything is working - but am not really sure on next steps.
what need to happen to make a 'normal' D10 version of the module?