commerce_donation_flow-1.0.x-dev/src/Plugin/Commerce/CheckoutFlow/DonationCheckoutFlow.php

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

namespace Drupal\commerce_donation_flow\Plugin\Commerce\CheckoutFlow;

use Drupal\commerce\Response\NeedsRedirectException;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowWithPanesBase;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowWithPanesInterface;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * A custom checkout flow for donations.
 *
 * @CommerceCheckoutFlow(
 *  id = "donation_checkout_flow",
 *  label = @Translation("Donation Flow"),
 * )
 */
class DonationCheckoutFlow extends CheckoutFlowWithPanesBase implements CheckoutFlowWithPanesInterface {

  /**
   * Injected service.
   *
   * @var \Drupal\Core\Session\AccountProxy
   */
  protected $user;

  /**
   * Drupal\Core\Routing\CurrentRouteMatch definition.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * Current default currency code.
   *
   * @var string
   */
  protected $currency;

  /**
   * Injected service.
   *
   * @var \CommerceGuys\Intl\Formatter\CurrencyFormatterInterface
   */
  protected $currencyFormatter;

  /**
   * The current route match.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * {@inheritdoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $pane_id,
    $pane_definition,
  ) {
    $instance = parent::create(
      $container,
      $configuration,
      $pane_id,
      $pane_definition
    );
    $instance->user = $container->get('current_user');
    $instance->requestStack = $container->get('request_stack');
    $instance->currencyFormatter = $container->get(
      'commerce_price.currency_formatter'
    );
    $instance->routeMatch = $container->get('current_route_match');
    if ($instance->order instanceof OrderInterface) {
      $instance->currency = $instance->order->getStore()
        ->getDefaultCurrencyCode();
    }

    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    $config = parent::defaultConfiguration();
    $config['offline_link_title'] = '';
    $config['offline_link_url'] = '';

    return $config;
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(
    array $form,
    FormStateInterface $form_state,
  ) {
    $form = parent::buildConfigurationForm($form, $form_state);
    $link_title = $this->configuration['offline_link_title'];
    $link_url = $this->configuration['offline_link_url'];
    $form['offline_link_title'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Offline donation link title'),
      '#default_value' => empty($link_title) ? '' : $link_title,
    ];

    $form['offline_link_url'] = [
      '#type' => 'url',
      '#title' => $this->t('Offline donation link URL'),
      '#default_value' => empty($link_url) ? '' : $link_url,
      '#description' => 'You may provide a link to a PDF or other file type that your users may download and use to send donations by mail.',
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(
    array &$form,
    FormStateInterface $form_state,
  ) {
    parent::submitConfigurationForm($form, $form_state);
    if (!$form_state->getErrors()) {
      $values = $form_state->getValue($form['#parents']);
      $this->configuration['offline_link_title'] = $values['offline_link_title'];
      $this->configuration['offline_link_url'] = $values['offline_link_url'];
    }
  }

  /**
   * {@inheritdoc}
   */
  public function redirectToStep($step_id) {
    // May throw a redirect exception with settings to commerce checkout.
    try {
      parent::redirectToStep($step_id);
    }
    catch (NeedsRedirectException $e) {
      // Use our controller instead.
      throw new NeedsRedirectException(
        Url::fromRoute(
          'commerce_donation_flow.donation_controller_formPage',
          [
            'commerce_order' => $this->order->id(),
            'step' => $step_id,
          ]
        )->toString()
      );
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getSteps() {
    // 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 =
      [
        'donation' => [
          'label' => $this->t('Your Donation'),
          'next_label' => $this->t('Continue'),
        ],
        'dedication' => [
          'label' => $this->t('Dedication Information'),
          'next_label' => $this->t('Continue'),
        ],
        'billing' => [
          'label' => $this->t('Billing Information'),
          'next_label' => $this->t('Continue to Payment'),
        ],
      ]
      + parent::getSteps();
    // Adjust names to match our use case:
    $steps['payment']['next_label'] = $this->t('Donate');

    if ($this->order instanceof OrderInterface && $this->order->hasItems()) {
      $items = $this->order->getItems();
      // Only one donation OrderItem supported.
      $donations = array_filter(
        $items,
        function ($item) {
          return $item->bundle() == 'donation';
        }
      );
      $donation = reset($donations);
      if ($donation instanceof OrderItemInterface
          && $donation->hasField(
          'field_donation_amount'
        ) && !$donation->field_donation_amount->isEmpty()) {
        // Append the amount on the payment button text.
        $amountFormatOptions = [
          'currency_display' => 'symbol',
          'minimum_fraction_digits' => 0,
        ];
        $donationAmount = $donation->field_donation_amount->first()->toPrice();
        $displayAmount = $this->currencyFormatter->format(
          $donationAmount->getNumber(),
          $this->currency,
          $amountFormatOptions
        );
        // Check for monthly.
        if (
          $donation->hasField('field_gift_type')
          && !$donation->field_gift_type->isEmpty()
          && $donation->field_gift_type->first()->value == 'recurring') {
          $displayAmount .= '/mo';
        }
        $steps['payment']['next_label'] = $this->t(
          'Donate @amount',
          ['@amount' => $displayAmount]
        );
      }
    }
    // All steps in the donation flow have no sidebar.
    foreach ($steps as &$step) {
      $step['has_sidebar'] = FALSE;
    }

    return $steps;
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(
    array $form,
    FormStateInterface $form_state,
    $step_id = NULL,
  ) {
    $this->order->recalculateTotalPrice();
    // Get the basic form.
    $form = parent::buildForm($form, $form_state, $step_id);
    $form['#attributes']['autocomplete'] = 'off';
    // Now build the summaries, if there are any.
    if ($form['#step_id'] != 'complete') {
      $summarySteps = $this->panesToSummarize($form['#step_id'], $form_state);
      $this->buildSummaries($form, $summarySteps);
    }
    // Identify the step in a class for analytics.
    $form['actions']['next']['#attributes']['class'][] = Html::cleanCssIdentifier(
      "donation-flow-step--$step_id"
    );
    $form['#attributes']['class'][] = Html::cleanCssIdentifier(
      "donation-flow-step--$step_id"
    );
    $link_url = $this->configuration['offline_link_url'] ?? NULL;
    if ($link_url && $step_id == 'donation') {
      $link_title = $this->configuration['offline_link_title'];
      $form['pdf_link'] = [
        '#title' => $link_title,
        '#type' => 'link',
        '#url' => Url::fromUri($link_url),
        '#weight' => 200,
        '#attributes' => [
          'target' => '_blank',
          'rel' => 'noopener noreferrer',
          'class' => ['pdf-link'],
        ],
        '#theme_wrappers' => ['container'],
      ];
    }

    return $form;
  }

  /**
   * Utility method to generate a step url.
   *
   * Used in this flow in and building the summary.
   *
   * @param string $step
   *   The step ID.
   *
   * @return \Drupal\Core\Url
   *   The url to the step.
   *
   * @see \Drupal\commerce_donation_flow\Plugin\Commerce\CheckoutPane\DonationItemPaneBase::doSummaryBuild
   */
  public function getStepUrl($step) {
    $options = [];
    $query = $this->requestStack->getCurrentRequest()->query;
    if ($query->has('donate_return')) {
      $returnPath = ['donate_return' => $query->get('donate_return')];
      $options = ['query' => $returnPath];
    }

    return Url::fromRoute(
      'commerce_donation_flow.donation_controller_formPage',
      [
        'commerce_order' => $this->order->id(),
        'step' => $step,
      ],
      $options
    );
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Steps are dynamic based on the submission, so clear to recalculate.
    unset($this->visibleSteps);
    parent::submitForm($form, $form_state);
    // Override the parent redirect data.
    $nextStepId = $this->getNextStepId($form['#step_id']);
    $form_state->setRedirectUrl($this->getStepUrl($nextStepId));
  }

  /**
   * Sort through panes to populate an array of panes to summarize.
   *
   * Only panes in visible steps should summarize.
   *
   * @param string $current_step
   *   The current step id.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The current form state.
   *
   * @return \Drupal\commerce_donation_flow\Plugin\Commerce\CheckoutFlow\PaneSummaryData[]
   *   The summary data.
   */
  protected function panesToSummarize(
    $current_step,
    FormStateInterface $formState,
  ) {
    /** @var \Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneInterface[] $possiblePanes */
    $possiblePanes = array_filter(
      $this->getPanes(),
      function ($pane) {
        return !in_array($pane->getStepId(), ['_sidebar', '_disabled']);
      }
    );
    $visible = $this->getVisibleSteps();
    $stepIds = array_keys($visible);
    $currentIndex = array_search($current_step, $stepIds);
    $indices = array_flip($stepIds);
    $stepsCompleted = [];
    foreach ($indices as $step => $index) {
      if ($index < $currentIndex) {
        $stepsCompleted[$step] = TRUE;
      }
    }
    if (isset($indices["complete"])) {
      // Don't list the 'thank you' page.
      unset($indices["complete"]);
    }
    $summaries = [];
    $step_number = 1;
    foreach ($indices as $step => $index) {
      if (isset($stepsCompleted[$step])) {
        $panes = array_filter(
          $possiblePanes,
          function ($pane) use ($step) {
            return $pane->getStepId() === $step;
          }
        );
        $summaries[$step] = new PaneSummaryData(
          $step_number,
          $visible[$step]['label'],
          $this->getStepUrl($step),
          $panes
        );
      }
      ++$step_number;
    }

    return $summaries;
  }

  /**
   * Helper method to build the full summary display and complete overview.
   *
   * @param array $form
   *   The form array being built.
   * @param \Drupal\commerce_donation_flow\Plugin\Commerce\CheckoutFlow\PaneSummaryData[] $summarySteps
   *   Array with summary data.
   */
  protected function buildSummaries(array &$form, array $summarySteps) {
    $form['summaries'] = [
      '#theme' => 'commerce_donation_flow_summary_container',
      '#weight' => -80,
      '#attributes' => [
        'class' => ['donation-summaries'],
      ],
      '#content' => [],
    ];
    // Adapted from Review::buildPaneForm.
    foreach ($summarySteps as $summaryStep => $data) {
      $form['summaries']['#content'][$summaryStep] = [
        'summary' => [],
        'edit_link' => [
          '#title' => $this->t('Edit'),
          '#type' => 'link',
          '#url' => $data->url,
          '#attributes' => [
            'class' => [
              'summary-edit',
              'edit-step--' . Html::cleanCssIdentifier(strtolower($summaryStep)),
            ],
          ],
        ],
        '#type' => 'container',
        '#attributes' => [
          'class' => [
            'summary--' . Html::cleanCssIdentifier(strtolower($summaryStep)),
          ],
        ],
      ];
      if ($data->hasPanes()) {
        foreach ($data->panes as $pane) {
          $form['summaries']['#content'][$summaryStep]['summary'][] = $pane->buildPaneSummary();
        }
      }
    }
  }

  /**
   * Utility method used to insure an order is set in this class.
   *
   * @param \Drupal\commerce_order\Entity\OrderInterface $order
   *   The order.
   */
  public function conditionalOrderValue(OrderInterface $order) {
    if (!$this->order instanceof OrderInterface) {
      $this->order = $order;
    }
  }

}

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

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