commerce_paypal-8.x-1.0-beta11/src/Plugin/Commerce/CheckoutFlow/Fastlane.php

src/Plugin/Commerce/CheckoutFlow/Fastlane.php
<?php

namespace Drupal\commerce_paypal\Plugin\Commerce\CheckoutFlow;

use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowWithPanesBase;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowWithPanesInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Markup;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a custom checkout flow for use by Fastlane by PayPal.
 *
 * @CommerceCheckoutFlow(
 *   id = "paypal_fastlane",
 *   label = "Fastlane by PayPal Checkout Flow",
 * )
 */
class Fastlane extends CheckoutFlowWithPanesBase {

  /**
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected ModuleHandlerInterface $moduleHandler;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $pane_id, $pane_definition): CheckoutFlowWithPanesInterface {
    $instance = parent::create($container, $configuration, $pane_id, $pane_definition);
    $instance->moduleHandler = $container->get('module_handler');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
    $form = parent::buildConfigurationForm($form, $form_state);
    if (isset($form['display_checkout_progress'])) {
      $form['display_checkout_progress']['#access'] = FALSE;
    }
    if (isset($form['display_checkout_progress_breadcrumb_links'])) {
      $form['display_checkout_progress_breadcrumb_links']['#access'] = FALSE;
    }
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function getSteps(): array {
    // Note that previous_label and next_label are not the labels
    // shown on the step itself. Instead, they are the labels shown
    // when going back to the step or proceeding to the step.
    $steps = [
      'order_information' => [
        'label' => $this->t('Order information'),
        'has_sidebar' => TRUE,
        'previous_label' => $this->t('Go back'),
      ],
    ] + parent::getSteps();
    $steps['complete']['next_label'] = $this->t('Pay and complete purchase');
    return $steps;
  }

  /**
   * {@inheritdoc}
   */
  public function getPanes(): array {
    $panes = parent::getPanes();
    $remove_list = ['paypal_checkout_payment_process', 'review', 'login', 'contact_information'];
    foreach ($panes as $id => $pane) {
      if (in_array($id, $remove_list, TRUE)) {
        unset($panes[$id]);
        continue;
      }
      // Ensure we don't override the existing configuration for these panes.
      if (!isset($this->configuration['panes'][$id])) {
        $pane->setStepId('_disabled');
      }
    }
    return $panes;
  }

  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void {
    parent::validateConfigurationForm($form, $form_state);
    $values = $form_state->getValue($form['#parents']);
    $pane_values = $values['panes'];
    $required_pane_ids = ['paypal_fastlane_contact_information'];
    $has_shipping = $this->moduleHandler->moduleExists('commerce_shipping');
    if ($has_shipping) {
      $required_pane_ids[] = 'shipping_information';
    }
    $required_pane_ids[] = 'payment_information';

    foreach ($required_pane_ids as $pane_id) {
      if (!isset($pane_values[$pane_id]) ||
        $pane_values[$pane_id]['step_id'] !== 'order_information') {
        $pane = $this->getPane($pane_id);
        $form_state->setError($form['panes'], $this->t('The %title pane must be configured in the %step step.', [
          '%title' => $pane->getLabel(),
          '%step' => $this->getStepLabel('order_information'),
        ]));
      }
    }

    $order_information_panes = array_filter($pane_values, static function ($pane_value) {
      return $pane_value['step_id'] === 'order_information';
    });
    $ordered_panes = array_keys($order_information_panes);

    $contact_pane_key = array_search('paypal_fastlane_contact_information', $ordered_panes, TRUE);
    if ($contact_pane_key === FALSE) {
      $form_state->setError($form['panes'], $this->t('The %title pane must be configured in the %step step.', [
        '%title' => $this->getPane('paypal_fastlane_contact_information')?->getLabel(),
        '%step' => $this->getStepLabel('order_information'),
      ]));
      return;
    }
    $payment_pane_key = array_search('payment_information', $ordered_panes, TRUE);
    if ($payment_pane_key === FALSE) {
      $form_state->setError($form['panes'], $this->t('The %title pane must be configured in the %step step.', [
        '%title' => $this->getPane('payment_information')?->getLabel(),
        '%step' => $this->getStepLabel('order_information'),
      ]));
      return;
    }

    if ($has_shipping) {
      $shipping_pane_key = array_search('shipping_information', $ordered_panes, TRUE);

      if ($shipping_pane_key === FALSE) {
        $form_state->setError($form['panes'], $this->t('The %title pane must be configured in the %step step.', [
          '%title' => $this->getPane('shipping_information')?->getLabel(),
          '%step' => $this->getStepLabel('order_information'),
        ]));
        return;
      }

      if ($shipping_pane_key < $contact_pane_key) {
        $form_state->setError($form['panes'], $this->t('The %title pane must be before the %title_2 pane.', [
          '%title' => $this->getPane('paypal_fastlane_contact_information')?->getLabel(),
          '%title_2' => $this->getPane('shipping_information')?->getLabel(),
        ]));
      }
      if ($payment_pane_key < $shipping_pane_key) {
        $form_state->setError($form['panes'], $this->t('The %title pane must be before the %title_2 pane.', [
          '%title' => $this->getPane('shipping_information')?->getLabel(),
          '%title_2' => $this->getPane('payment_information')?->getLabel(),
        ]));
      }
    }
    else {
      if ($payment_pane_key < $contact_pane_key) {
        $form_state->setError($form['panes'], $this->t('The %title pane must be before the %title_2 pane.', [
          '%title' => $this->getPane('paypal_fastlane_contact_information')?->getLabel(),
          '%title_2' => $this->getPane('payment_information')?->getLabel(),
        ]));
      }
    }

    if ($payment_pane_key < (count($ordered_panes) - 1)) {
      $form_state->setError($form['panes'], $this->t('The %title pane must be the last pane.', [
        '%title' => $this->getPane('payment_information')?->getLabel(),
      ]));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, $step_id = NULL): array {
    $form['#attached']['library'][] = 'commerce_paypal/paypal_fastlane';

    // @todo We are relying on the array parents of the shipping profile.
    // We need to do this so that the shipping profile always renders
    // as a form, rather than a rendered profile.
    // See: /commerce/modules/order/src/Plugin/Commerce/InlineForm/CustomerProfile.php::shouldRender
    $form_state->set([
      'shipping_information',
      'shipping_profile',
      'render',
    ], FALSE);
    $form = parent::buildForm($form, $form_state, $step_id);
    if ($step_id !== 'order_information') {
      return $form;
    }

    // Add pane summaries.
    $weight = 1;
    foreach ($this->getVisiblePanes($step_id) as $pane_id => $pane) {
      // Tweak the visible panes.
      $form[$pane_id]['#weight'] = $weight++;
      $form[$pane_id]['#attributes']['data-checkout-pane-id'] = $pane_id;

      // Build the summary for each pane.
      $pane_summary = $pane->buildPaneSummary();
      if (empty($pane_summary)) {
        $pane_summary['summary'] = [
          '#type' => 'container',
        ];
      }

      // Add a summary to each pane.
      $form[$pane_id]['summary'] = [
        '#type' => 'container',
        0 => $pane_summary,
        '#weight' => -10,
        '#attributes' => ['class' => ['summary']],
      ];

      // Add an edit button to each pane.
      $form[$pane_id]['button_edit'] = [
        '#markup' => Markup::create('<button class="button btn btn-primary" type="button" data-checkout-button-type="edit">' . $this->t('Edit') . '</button>'),
        '#weight' => -9,
      ];
      if ($pane_id !== 'payment_information') {
        // Add a "continue" button to each pane,
        // except the payment_information pane, which must be the last pane.
        $form[$pane_id]['button_continue'] = [
          '#markup' => Markup::create('<button class="button btn btn-primary" type="button" data-checkout-button-type="continue">' . $this->t('Continue') . '</button>'),
        ];
      }
      if ($pane_id === 'shipping_information') {
        // Add a change button for use by PayPal Fastlane members.
        $form[$pane_id]['button_change'] = [
          '#markup' => Markup::create('<button class="button btn btn-primary" type="button" data-checkout-button-type="change">' . $this->t('Change') . '</button>'),
          '#weight' => -8,
        ];
        // Remove elements that cause rendering issues.
        unset($form[$pane_id]['#wrapper_id'], $form[$pane_id]['#prefix'], $form[$pane_id]['#suffix']);
        // We do not want auto recalculate to run on this form.
        $form[$pane_id]['#auto_recalculate'] = FALSE;
        $profiles = $this->order->collectProfiles();
        if (array_key_exists('shipping', $profiles)) {
          $shipping_profile = $profiles['shipping'];
          if ($shipping_profile && !$shipping_profile->get('address')->isEmpty()) {
            $shipping_address = $shipping_profile->get('address')?->first()?->getValue();
            $form[$pane_id]['#attached']['drupalSettings']['paypalFastlane'][$this->getOrder()?->id()]['shippingInformation']['address'] = $shipping_address;
          }
          // @todo we should send phone, if one is present.
        }
      }
    }
    self::replaceAjaxRefreshForm($form);
    return $form;
  }

  /**
   * Replace the ajax refresh form callback with our own.
   *
   * @param array $form
   *   The form array.
   */
  protected static function replaceAjaxRefreshForm(array &$form): void {
    foreach ($form as $key => &$value) {
      // Check if the current value is an array (non-leaf node).
      if (is_array($value)) {
        // Recursively call the function for the child array.
        self::replaceAjaxRefreshForm($value);
      }

      // Change callback to our own.
      if ($key === '#ajax') {
        if ($value['callback'][1] === 'ajaxRefreshForm') {
          $value['callback'][0] = self::class;
        }
      }
    }
  }

  /**
   * Ajax refresh form callback.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   The ajax response.
   */
  public static function ajaxRefreshForm(array $form, FormStateInterface $form_state) {
    $triggering_element = $form_state->getTriggeringElement();
    $element = NULL;
    if (!empty($triggering_element['#ajax']['element'])) {
      $element = NestedArray::getValue($form, $triggering_element['#ajax']['element']);
    }
    // Element not specified or not found. Show messages on top of the form.
    if (!$element) {
      $element = $form;
    }
    $response = new AjaxResponse();
    foreach (Element::children($form) as $key) {
      self::processRefreshChildren($response, $form[$key], $key);
    }
    $response->addCommand(new PrependCommand('[data-drupal-selector="' . $element['#attributes']['data-drupal-selector'] . '"]', ['#type' => 'status_messages']));

    return $response;
  }

  /**
   * Process the refresh child elements.
   *
   * @param \Drupal\Core\Ajax\AjaxResponse $response
   *   The ajax response.
   * @param array $element
   *   The form element.
   * @param string $key
   *   The key.
   */
  protected static function processRefreshChildren(AjaxResponse $response, array $element, string $key): void {
    if (in_array($key, ['paypal_fastlane_contact_information', 'payment_information', 'actions'], TRUE)) {
      return;
    }
    if ($key === 'sidebar') {
      foreach (Element::children($element) as $child_key) {
        self::processRefreshChildren($response, $element[$child_key], $child_key);
      }
      return;
    }
    if (!isset($element['#attributes']['data-drupal-selector'])) {
      return;
    }
    $response->addCommand(new ReplaceCommand('[data-drupal-selector="' . $element['#attributes']['data-drupal-selector'] . '"]', $element));
  }

  /**
   * Gets the step label.
   *
   * @param string $step_id
   *   The step id.
   *
   * @return string
   *   The step label.
   */
  protected function getStepLabel(string $step_id): string {
    $label = $step_id;
    $steps = $this->getSteps();
    if (array_key_exists($step_id, $steps)) {
      $label = $steps[$step_id]['label'];
    }
    return $label;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc