Trying to populate a ticket with values from the associated product - devel shows no data in the fields?

Created on 5 March 2024, 10 months ago
Updated 21 April 2024, 8 months ago

Problem/Motivation

I am trying to add data from the fields on the saleable product (based on ticket) , to an instance of a sold ticket.
I *think* I am doing this correctly, but any insight is appreciated in case I am barking up the wrong tree.

My deadline for this was/is today so am cutting it fine (based on a local event they I am trying to use the tickets for!).

I have a saleable ticket that works, and am correctly generating a QR code on the ticket. I am unable to populate the ticket with further static or dynamic data.

These are the steps I am currently using:

My saleable Product (based on ticket type) has two custom fields.

Event Title
Event Date and Time.
(as per image, these are correctly populated).

My product display, for the product type, now has a custom display called ticket, with only those items added. (via /admin/commerce/config/product-types/ticket/edit/display/ticket)

If I visit: /admin/structure/commerce_ticket_types and edit the default ticket type, I can add a new field.

I have added a new field called 'Event Details', which is an entity reference field, referencing a product item. The product type is set to ticket.

Under manage display (/admin/structure/commerce_ticket_types/manage/default/display) for the default ticket I have enabled and added the 'Event Details' field, set it to have a format of rendered entity, and to be (via view mode) 'rendered as ticket'.

My understanding is:

1 - The act of creating the ticket (triggered during the checkout process) should have updated all of the fields on the ticket? (this may be in error?)
2 - The combination of custom field, entity reference and display mode should now try an render the fields from the (related) product type, onto the fieldable entity of the ticket.
3 - Devel indicates that although the field structure is there, there is no data in the array?

(note: if I can get all of this working I am happy to try and write up a more comprehensive guide to this process.)

So the key questions:

1 - Is my understanding correct, and am I going about this the right way? Is there a better way to populate the ticket with information?

2 - This is the approach (I think) for static information, I am still trying to work out how to provide a prompt in the checkout for dynamic information (in this instance the name of the ticket holder, one person may buy 4 tickets, and we will need player names and ID numbers)

PS: I am currently wondering if this is down to my understanding of how the field get populated (or not). I may try ECA or some other method to try and update the field!

💬 Support request
Status

Active

Version

2.0

Component

Documentation

Created by

🇬🇧United Kingdom martin@manarock.co.uk

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • 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?

Production build 0.71.5 2024