How can you contact us

2By phone at +40 749 014 360

If you still have problems, please let us know, by sending an email to info@westweb-solutions.com Thank you!

OFFICE HOURS

Mon-Fri 9:00AM - 6:00AM
Sat - 9:00AM - 1:00PM
Sundays by appointment only!

Drupal 8 - Ticketmaster Import

/ / Drupal 8

One of our clients needed integration with https://www.ticketmaster.ca/ (if the page is not working try using a VPN and set it to Canada).
We only had to import the events from this 3rd party, there were no requests for the actual ticket buying.

These being said, let's get down to business.

Step 1.

Create a custom module, let's call it "Custom Ticketmaster" (I like doing this by using the Drupal Console, it's a less time-consuming method that the classic one in which you do it all by your own).
We'll need a configuration form and a queue worker (plugin), both of these can be created using the Drupal Console.

Step 2.

Check the routing file custom_ticketmaster.routing.yml. Here you will find a path, a form that will be loaded on that page and the permission tag which is used to restrict access.

custom_ticketmaster.custom_ticketmaster_configuration:
  path: '/admin/config/custom-ticketmaster-configuration'
  defaults:
    _form: '\Drupal\custom_ticketmaster\Form\ConfigurationForm'
    _title: 'Custom Ticketmaster Configuration'
  requirements:
    _permission: 'custom ticketmaster configuration'

The permission is defined in custom_ticketmaster.permissions.yml

custom ticketmaster configuration:
  title: 'Administer Custom Ticketmaster Configuration'
  description: 'Administer Custom Ticketmaster Configuration.'

 

Step 3.

Let's start working on the configuration form (/src/Form/ConfigurationForm.php).

ticketmaster configuration 1

ticketmaster configuration 2

Here you can configure and see the requirements you need in order to use the Ticketmaster API. You may set how many items should be imported during the cron and the frequency of the cron.
You may also force a Ticketmaster import for a limited number of items or force the full cron run even if this is not scheduled for another hour or more. The default options are set in /config/install/custom_ticketmaster.settings.yml

interval: 300
ticketmaster_api_url: https://app.ticketmaster.com/discovery/v2/events
ticketmaster_api_key: API_KEY_LOREM_IPSUM
ticketmaster_classification: Comedy,Music,Theatre,Festival
ticketmaster_country_code: CA
ticketmaster_market_id: 102

The configuration form contains the standard form definition and the following submit functions:

 

3.1. Cron Submit 

- this will force the cron to be run, even if it's not scheduled for another hour or more

  /**
   * Allow user to directly execute cron, optionally forcing it.
   */
  public function cronRun(array &$form, FormStateInterface &$form_state) {
    $config = $this->configFactory->getEditable('custom_ticketmaster.settings');

    $cron_reset = $form_state->getValue('cron_reset');
    if (!empty($cron_reset)) {
      // \Drupal::state()->set('custom_ticketmaster.next_execution', 0);
      $this->configFactory->getEditable('custom_ticketmaster.next_execution')->set('next_execution', 0);
    }

    // Use a state variable to signal that cron was run manually from this form.
    $this->state->set('custom_ticketmaster_show_status_message', TRUE);
    if ($this->cron->run()) {
      $this->configFactory->getEditable('custom_ticketmaster.next_execution')->set('next_execution', REQUEST_TIME);
      drupal_set_message($this->t('Cron ran successfully.'));
    }
    else {
      drupal_set_message($this->t('Cron run failed.'), 'error');
    }
  }

 

3.2. addItems 

- this will add new items to the queue that will be used on cron (this function was mainly used during development and for testing purposes)

  /**
   * Add the items to the queue when signaled by the form.
   */
  public function addItems(array &$form, FormStateInterface &$form_state) {
    $values = $form_state->getValues();
    $queue_name = $form['cron_queue_setup']['queue'][$values['queue']]['#title'];
    $num_items = $form_state->getValue('num_items');
    // Queues are defined by a QueueWorker Plugin which are selected by their
    // id attritbute.
    // @see \Drupal\custom_ticketmaster\Plugin\QueueWorker\ReportWorkerOne
    $queue = $this->queue->get($values['queue']);

    for ($i = 1; $i <= $num_items; $i++) {
      // Create a new item, a new data object, which is passed to the
      // QueueWorker's processItem() method.
      $item = new \stdClass();
      $item->created = REQUEST_TIME;
      $item->sequence = $i;


      $queue->createItem($item);
    }

    $args = [
      '%num' => $num_items,
      '%queue' => $queue_name,
    ];
    drupal_set_message($this->t('Added %num items to %queue', $args));
  }

 

3.3. submitForm 

- the standard submit that will save the variables


  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Update the interval as stored in configuration. This will be read when
    // this modules hook_cron function fires and will be used to ensure that
    // action is taken only after the appropiate time has elapsed.
    $this->configFactory->getEditable('custom_ticketmaster.settings')
      ->set('interval', $form_state->getValue('custom_ticketmaster_interval'))
      ->set('ticketmaster_api_url', $form_state->getValue('ticketmaster_api_url'))
      ->set('ticketmaster_api_key', $form_state->getValue('ticketmaster_api_key'))
      ->set('ticketmaster_classification', $form_state->getValue('ticketmaster_classification'))
      ->set('ticketmaster_country_code', $form_state->getValue('ticketmaster_country_code'))
      ->set('ticketmaster_market_id', $form_state->getValue('ticketmaster_market_id'))
      ->save();

    // parent::submitForm($form, $form_state);
  }


 

Step 4. QueueWorker

As you may have noticed in 3.2, we've used a Queue (the same one will be used in the cron implementation).
This is defined in /src/Plugin/QueueWorker/ImportTicketmasterEventsWorker.php. It will receive an event from Ticketmaster and convert its data to Drupal entities.
I'll get right into the processItem function.

 

4.1. Check for an existing event

First of all, we'll need to make a search by the Ticketmaster identifier for any existing event in our system.

      $existing_nodes = \Drupal::entityTypeManager()
        ->getStorage('node')
        ->loadByProperties(['type' => 'event', 'field_ticketmaster_id' => $event['id']]);

      if (! $node = reset($existing_nodes)) {
        // Event has not been previously imported.

        // Create event node.
        $event_node = Node::create([
          'nid' => NULL,
          'type' => 'event',
          'title' => $event['name'],
          'uid' => 1,
          // 'status' => TRUE,
          'status' => FALSE,
        ]);

4.2. Add text fields and images

Change the following to your own needs.

        if (isset($event['images']) && $event['images']) {
          // Set Banners.
          if (isset($event['images'][9]['url'])) {
            //Save Image in local from remote data.
            $data = file_get_contents($event['images'][9]['url']);
            $image_name = explode('/', $event['images'][9]['url']);
            $file = file_save_data($data, 'public://event/' . $image_name[count($image_name) - 1], FILE_EXISTS_REPLACE);
            $event_node->set('field_banners', [ 'target_id' => $file->id(), 'alt' => $event['name' ]]);
          }
          // Set Thumbnail.
          if (isset($event['images'][2]['url'])) {
            //Save Image in local from remote data.
            $data = file_get_contents($event['images'][2]['url']);
            $image_name = explode('/', $event['images'][2]['url']);
            $file = file_save_data($data, 'public://event/' . $image_name[count($image_name) - 1], FILE_EXISTS_REPLACE);
            $event_node->set('field_thumbnail', [ 'target_id' => $file->id(), 'alt' => $event['name' ]]);
          }
        }

        // Set Body.
        $body = NULL;

        // Set info.
        if (isset($event['info']) && $event['info']) {
          $body = $event['info'];
        }

        // Set pleaseNote.
        if (isset($event['pleaseNote']) && $event['pleaseNote']) {
          $body .= '<div id="pleaseNote">';
          $body .= '<h2>Please Note</h2>';
          $body .= $event['pleaseNote'];
          $body .= '</div>';
        }

        $event_node->set('body', [ 'value' => $body, 'format' => 'full_html' ]);

        // Set Price Ranges.
        if (isset($event['priceRanges'][0]) && $event['priceRanges'][0]) {
          if (isset($event['priceRanges'][0]['min'])) {
            $priceRanges = $event['priceRanges'][0]['min'] . ' - ' . $event['priceRanges'][0]['max'] . ' ' . $event['priceRanges'][0]['currency'] . ' (' . $event['priceRanges'][0]['type'] . ')';
          }
          else {
            $priceRanges = $event['priceRanges'][0]['max'] . ' ' . $event['priceRanges'][0]['currency'] . ' (' . $event['priceRanges'][0]['type'] . ')';
          }
          $event_node->set('field_ticket_price_or_cover', $priceRanges);
        }

4.3. Classification

As you may guess, the classifications are represented by hierarchical taxonomy terms in our system (e.g. Music -> Rock). Since we are dealing with a small number of classifications, we used php constants for these terms.

define('CUSTOM_TICKETMASTER_CONFIGURATION_VID', 'configuration');
define('CUSTOM_TICKETMASTER_SHOW_TYPE_VID', 'show_type');
define('CUSTOM_TICKETMASTER_VENUE_TYPE_VID', 'venue_type');

define('CUSTOM_TICKETMASTER_MAIN_SHOW_TYPE_MUSIC_TID', 2);

define('CUSTOM_TICKETMASTER_SHOW_TYPE_MUSIC_TID', 11);

Next, we'll need to add the main classification and its sub-classification to the Drupal event. If the term representing the sub-classification does not exist in our system we'll have to create it.

        // Load/create show type taxonomy term.
        $show_type_terms = [];
        if ($show_type_term_name) {
          if ($show_type_term_name != 'Undefined') {
            $terms = \Drupal::entityTypeManager()
              ->getStorage('taxonomy_term')
              ->loadByProperties(['name' => $show_type_term_name, 'vid' => CUSTOM_TICKETMASTER_SHOW_TYPE_VID]);
            if (! $show_type_term = reset($terms)) {
              $show_type_term = Term::create([
                'name' => $show_type_term_name,
                'vid' => CUSTOM_TICKETMASTER_SHOW_TYPE_VID,
              ])->save();
            }

            switch ($show_type_term_name) {
              case 'Arts & Theatre':
                // Set Main Show Type: Live Theatre
                $event_node->set('field_main_show_type', ['target_id' => CUSTOM_TICKETMASTER_MAIN_SHOW_TYPE_THEATRE_TID]);
                break;

              case 'Comedy':
                // Set Main Show Type: Live Comedy
                $event_node->set('field_main_show_type', ['target_id' => CUSTOM_TICKETMASTER_MAIN_SHOW_TYPE_COMEDY_TID]);
                break;

              case 'Festivals':
                // Set Main Show Type: Live Events
                $event_node->set('field_main_show_type', ['target_id' => CUSTOM_TICKETMASTER_MAIN_SHOW_TYPE_EVENTS_TID]);
                break;

              case 'Music':
                // Set Main Show Type: Live Music
                $event_node->set('field_main_show_type', ['target_id' => CUSTOM_TICKETMASTER_MAIN_SHOW_TYPE_MUSIC_TID]);
                break;
            }
          }
        }
        if (isset($event['classifications'][0]['genre']['name']) && $show_type_term_name = $event['classifications'][0]['genre']['name']) {
          if ($show_type_term_name != 'Undefined') {
            $terms = \Drupal::entityTypeManager()
              ->getStorage('taxonomy_term')
              ->loadByProperties(['name' => $show_type_term_name, 'vid' => CUSTOM_TICKETMASTER_SHOW_TYPE_VID]);
            if (! $show_type_term = reset($terms)) {
              $show_type_term = Term::create([
                'name' => $show_type_term_name,
                'vid' => CUSTOM_TICKETMASTER_SHOW_TYPE_VID,
              ])->save();
            }
            $show_type_terms = [ $show_type_term ];
          }
        }
        if ($show_type_terms) {
          $event_node->set('field_show_type', $show_type_terms);
        }

4.4. Artist & Venue

We'll proceed as above with the artist and the venue. In our system, these entities are represented by nodes.
For the venues, we'll use the Ticketmaster identifier.

        // Load/create venue node.
        if ($venue = $event['_embedded']['venues'][0]) {
          $venues = \Drupal::entityTypeManager()
            ->getStorage('node')
            ->loadByProperties(['type' => 'venue', 'field_ticketmaster_id' => $venue['id']]);
          if (! $venue_node = reset($venues)) {


            $venues = \Drupal::entityTypeManager()
              ->getStorage('node')
              ->loadByProperties(['title' => ( isset($venue['name']) ? $venue['name'] : $venue['id'] ), 'type' => 'venue']);
            foreach ($venues as $venue_node) {
              $address = $venue_node->get('field_address')->getValue();
              // Check for postal code.
              if ($address[0]['postal_code'] == $venue['postalCode']) {
                break;
              }
            }
            if (! $venue_node) {
              // Create venue node.
              $venue_node = Node::create([
                'nid' => NULL,
                'type' => 'venue',
                'title' => isset($venue['name']) ? $venue['name'] : $venue['id'],
                'uid' => 1,
                // 'status' => TRUE,
                'status' => FALSE,
              ]);
              $venue_node->set('field_ticketmaster_id', $venue['id']);
            }

            // Set address.
            $address = [
              'country_code' => $venue['country']['countryCode'],
              'administrative_area' => isset($venue['state']['stateCode']) ? $venue['state']['stateCode'] : NULL,
              'locality' => $venue['city']['name'],
              'postal_code' => $venue['postalCode'],
              'address_line1' => $venue['address']['line1'],
              'address_line2' => NULL,
              'organization' => NULL
            ];
            $venue_node->set('field_address', $address);

            if (isset($venue['location']) && $venue['location']) {
              $venue_node->set('field_geofield', [
                'latitude' => $venue['location']['latitude'],
                'longitude' => $venue['location']['longitude']
              ]);
              $venue_node->set('field_geolocation', [
                'latitude' => $venue['location']['latitude'],
                'longitude' => $venue['location']['longitude']
              ]);
            }

            $venue_node->save();
          }
          $event_node->set('field_venues', $venue_node);
        }

For the artist, we'll do a search by his name.

        // Set Attractions.
        if (isset($event['_embedded']['attractions'])) {
          $artists_references = [];
          foreach ($event['_embedded']['attractions'] as $attraction) {
            $artists = \Drupal::entityTypeManager()
              ->getStorage('node')
              ->loadByProperties(['type' => 'artist', 'title' => $attraction['name']]);
            if (! $artist_reference = reset($artists)) {
              // Create artist node.
              $artist_reference = Node::create([
                'nid' => NULL,
                'type' => 'artist',
                'title' => $attraction['name'],
                'uid' => 1,
                // 'status' => TRUE,
                'status' => FALSE,
              ]);
              $artist_reference->set('field_first_name', $attraction['name']);
              $artist_reference->save();
            }
            $artists_references[] = $artist_reference;
          }
          $event_node->set('field_artist', $artists_references);
        }

4.5. Save event and log messages/errors.

Add (or not) the log functions accordingly to your own logic (if/else).

$event_node->save();

\Drupal::logger('ImportTicketmasterEventsWorker')->notice('new event %title (%id)', [
  '%title' => isset($event['name']) ? $event['name'] : $event_node->id(),
  '%id' => $event_node->id(),
]);

\Drupal::logger('ImportTicketmasterEventsWorker')->notice('event %name exists', [
  '%name' => $event['name'],
]);

\Drupal::logger('ImportTicketmasterEventsWorker')->error('new event %title not imported - %show_type_term_name - event %event', [
  '%title' => isset($event['name']) ? $event['name'] : 'title not found',
  '%show_type_term_name' => $show_type_term_name,
  '%event' => $event
]);

 

5. Cron

5.1. Check for timestamp and get settings.

Ticketmaster only provides the first 50 pages of results.

/**
 * Implements hook_cron().
 */
function custom_ticketmaster_import_cron() {
  $last_run = \Drupal::state()->get('lm_import.last_run', 0);

  // If 60 minutes passed since last time.
  if ( ( ( \Drupal::time()->getRequestTime() - $last_run ) >= 3600 * 24 * 6 ) && ( date('w') == 0 ) ) {

    // We access our configuration.
    $config = \Drupal::config('custom_ticketmaster.settings');

    $ticketmaster_api_url = $config->get('ticketmaster_api_url');
    $ticketmaster_api_key = $config->get('ticketmaster_api_key');
    $ticketmaster_classification = $config->get('ticketmaster_classification');
    $ticketmaster_country_code = $config->get('ticketmaster_country_code');
    $ticketmaster_market_id = $config->get('ticketmaster_market_id');

    $clientFactory = \Drupal::service('http_client_factory');
    $client = $clientFactory->fromOptions(['verify' => FALSE]);

    $page = 0;
    $import = TRUE;
    $events_exists = TRUE;

    while ($import && $page < 50) {
      // Step 5.2.
    }

    // Update last run.
    \Drupal::state()->set('custom_ticketmaster.last_run', \Drupal::time()->getRequestTime());
  }
}

5.2. Get events and queue them

      try {
        $response = $client->request('GET', $ticketmaster_api_url, [
          'query' => [
            'apikey' => $ticketmaster_api_key,
            // 'marketId' => $ticketmaster_market_id,
            'classificationName' => $ticketmaster_classification,
            'countryCode' => $ticketmaster_country_code,
            'page' => $page
          ]
        ]);

        $data = $response->getBody();

        if (empty($data)) {
          \Drupal::logger('ImportTicketmasterEventsWorker')->notice('no new data', []);
        }
        else {
          $json_output = json_decode($data, TRUE);
          $events = $json_output['_embedded']['events'];
          $queue = \Drupal::queue('custom_ticketmaster_import_ticketmaster_events');
            foreach ($events as $event) {

            $existing_nodes = \Drupal::entityTypeManager()
              ->getStorage('node')
              ->loadByProperties(['type' => 'event', 'field_ticketmaster_id' => $event['id']]);

            if (! $node = reset($existing_nodes)) {
              // Event has not been previously imported.

              // Add Event to queue.
              $queue->createItem($event);

              $events_exists = $events_exists AND FALSE;
            }
            else {
              $events_exists = $events_exists AND TRUE;
            }
          }

          if ($events_exists) {
            $import = TRUE;
            $page++;
          }
          else {
            $import = FALSE;
          }
        }
      }
      catch (RequestException $e) {
        watchdog_exception('custom_ticketmaster', $e);
      }


Basically, that's it!

Sign in your account to have access to different features

Forgot your details?

TOP