arch-8.x-1.x-dev/modules/checkout/modules/onepage/src/Form/OnepageCheckoutForm.php

modules/checkout/modules/onepage/src/Form/OnepageCheckoutForm.php
<?php

namespace Drupal\arch_onepage\Form;

use Drupal\arch_cart\Cart\CartHandlerInterface;
use Drupal\arch_order\OrderAddressData;
use Drupal\arch_order\Services\OrderAddressServiceInterface;
use Drupal\arch_order\Services\OrderStatusService;
use Drupal\arch_payment\PaymentMethodManagerInterface;
use Drupal\arch_shipping\ShippingMethodInterface;
use Drupal\arch_shipping\ShippingMethodManagerInterface;
use Drupal\Component\Utility\Random;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\RedirectDestinationInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\user\Entity\User;
use Drupal\user\UserDataInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Implements a Onepage Checkout form.
 */
class OnepageCheckoutForm extends FormBase {

  /**
   * Current user object.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * User data.
   *
   * @var \Drupal\user\UserDataInterface
   */
  protected $userData;

  /**
   * Shipping method manager.
   *
   * @var \Drupal\arch_shipping\ShippingMethodManagerInterface
   */
  protected $shippingMethodManager;

  /**
   * Payment method manager.
   *
   * @var \Drupal\arch_payment\PaymentMethodManagerInterface
   */
  protected $paymentMethodManager;

  /**
   * Order.
   *
   * @var \Drupal\arch_order\Entity\Order
   */
  protected $order;

  /**
   * Order status service.
   *
   * @var \Drupal\arch_order\Services\OrderStatusService
   */
  protected $orderStatus;

  /**
   * Cart.
   *
   * @var \Drupal\arch_cart\Cart\CartInterface
   */
  protected $cart;

  /**
   * Current request object.
   *
   * @var null|\Symfony\Component\HttpFoundation\Request
   */
  protected $currentRequest;

  /**
   * Redirect destination object.
   *
   * @var \Drupal\Core\Routing\RedirectDestinationInterface
   */
  protected $destination;

  /**
   * Renderer object.
   *
   * @var \Drupal\Core\Render\Renderer|\Drupal\Core\Render\RendererInterface
   */
  protected $renderer;

  /**
   * Order address service.
   *
   * @var \Drupal\arch_order\Services\OrderAddressServiceInterface
   */
  protected $orderAddressService;

  /**
   * Language manager.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

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

  /**
   * Entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * User storage.
   *
   * @var \Drupal\user\UserStorageInterface
   */
  protected $userStorage;

  /**
   * OnepageCheckoutForm constructor.
   *
   * @param \Drupal\Core\Session\AccountInterface $current_user
   *   Current user object.
   * @param \Drupal\user\UserDataInterface $user_data
   *   User data.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   Module handler object.
   * @param \Drupal\arch_shipping\ShippingMethodManagerInterface $shipping_method_manager
   *   Shipping method manager.
   * @param \Drupal\arch_payment\PaymentMethodManagerInterface $payment_method_manager
   *   Payment method manager.
   * @param \Drupal\arch_cart\Cart\CartHandlerInterface $cart_handler
   *   Cart handler.
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   Request stack.
   * @param \Drupal\arch_order\Services\OrderStatusService $order_status
   *   Order status service.
   * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
   *   Redirect destination object.
   * @param \Drupal\Core\Render\RendererInterface $renderer
   *   Renderer object.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   Language manager.
   * @param \Drupal\arch_order\Services\OrderAddressServiceInterface $order_address_service
   *   Order address service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Entity type manager.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function __construct(
    AccountInterface $current_user,
    UserDataInterface $user_data,
    ModuleHandlerInterface $module_handler,
    ShippingMethodManagerInterface $shipping_method_manager,
    PaymentMethodManagerInterface $payment_method_manager,
    CartHandlerInterface $cart_handler,
    RequestStack $request_stack,
    OrderStatusService $order_status,
    RedirectDestinationInterface $redirect_destination,
    RendererInterface $renderer,
    LanguageManagerInterface $language_manager,
    OrderAddressServiceInterface $order_address_service,
    EntityTypeManagerInterface $entity_type_manager,
  ) {
    $this->currentUser = $current_user;
    $this->userData = $user_data;
    $this->moduleHandler = $module_handler;
    $this->currentRequest = $request_stack->getCurrentRequest();
    $this->destination = $redirect_destination;
    $this->renderer = $renderer;

    $this->shippingMethodManager = $shipping_method_manager;
    $this->paymentMethodManager = $payment_method_manager;
    $this->cart = $cart_handler->getCart();
    $this->order = $this->cart->getOrder();
    $this->orderStatus = $order_status;
    $this->orderAddressService = $order_address_service;
    $this->languageManager = $language_manager;

    $this->entityTypeManager = $entity_type_manager;
    $this->userStorage = $this->entityTypeManager->getStorage('user');
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('current_user'),
      $container->get('user.data'),
      $container->get('module_handler'),
      $container->get('plugin.manager.shipping_method'),
      $container->get('plugin.manager.payment_method'),
      $container->get('arch_cart_handler'),
      $container->get('request_stack'),
      $container->get('order.statuses'),
      $container->get('redirect.destination'),
      $container->get('renderer'),
      $container->get('language_manager'),
      $container->get('order.address'),
      $container->get('entity_type.manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'arch_onepage_checkout';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, $billing = NULL, $shipping = NULL) {
    $form['#prefix'] = '<div id="onepage-checkout-form-wrapper">';
    $form['#suffix'] = '</div>';

    $form['#attached']['library'][] = 'arch_onepage/onepage-checkout';

    $form['billing'] = $this->billingForm($billing);
    $form['shipping'] = $this->shippingForm($shipping, $form['billing']);
    $form['payment'] = $this->paymentForm();

    if ($this->currentUser->isAnonymous()) {
      $form['create_user'] = [
        '#type' => 'checkbox',
        '#attributes' => [
          'class' => [
            'create-user-option',
          ],
        ],
        '#default_value' => FALSE,
        '#title' => $this->t('Create new account', [], ['context' => 'arch_onepage']),
      ];
    }

    $form['actions'] = [
      '#type' => 'actions',
      'submit' => [
        '#type' => 'submit',
        '#value' => $this->t('Checkout', [], ['context' => 'arch_onepage']),
        '#attributes' => [
          'class' => [
            'btn-success',
          ],
        ],
      ],
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    // If AJAX request, we should not have to validate form, because we only
    // chose a payment menthod.
    if ($this->currentRequest->isXmlHttpRequest()) {
      return TRUE;
    }

    // Clear default errors because of the case if we chose the 'sameas' value
    // for Shipping Address, but the 'shipping_*' fields are remains required
    // which cause errors but it should not.
    $form_errors = $form_state->getErrors();
    $form_state->clearErrors();

    // Remove 'shipping_*' related errors.
    foreach ($form_errors as $key => $field) {
      if (strpos($key, 'shipping_') > -1) {
        unset($form_errors[$key]);
      }
    }

    // Re-apply rest of the errors.
    foreach ($form_errors as $name => $error_message) {
      $form_state->setErrorByName($name, $error_message);
    }

    // @todo Implement 'Accept TOC & Privacy' if it is in use.
    // @todo Check existing paymentmethod..
    if ($this->currentUser->isAnonymous()) {
      $found = $this->userStorage->loadByProperties(['mail' => $form_state->getValue('email')]);
      // Check that given email address is exist, or not.
      if (!empty($found)) {
        $form_state->setErrorByName(
          'email',
          $this->t(
            'The given email address is already taken. Please <a href="@url">log in</a> to use this email address.',
            [
              '@url' => Url::fromUserInput('/user/login', ['query' => ['destination' => 'checkout']])->toString(),
            ],
            ['context' => 'arch_onepage']
          )
        );
        $form_state->setRebuild();
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $orderExtraData = [];

    $order_currency = $this->order->get('currency')->getString();
    if (empty($order_currency)) {
      $order_currency = 'EUR';
    }

    /** @var \Drupal\arch_order\Entity\OrderStatusInterface[] $orderStatuses */
    $orderStatuses = $this->orderStatus->getOrderStatuses();
    if (isset($orderStatuses['checkout'])) {
      $this->order->set('status', $orderStatuses['checkout']->getId());
    }
    else {
      $defaultOrderStatus = $this->orderStatus->getDefaultOrderStatus();
      $this->order->set('status', $defaultOrderStatus->getId());
    }

    $user = $this->currentUser;
    if ($user->id() == 0 && $form_state->getValue('create_user') == 1) {
      $user = $this->createUser($form_state);
    }

    if (!empty($user)) {
      $this->order->setOwnerId($user->id());
    }

    $method = $form_state->getValue('shipping_methods');
    $this->order->set('shipping_method', $method);

    $paymentMethodId = $form_state->getValue('payment_method');
    $this->order->set('payment_method', $paymentMethodId);
    try {
      /** @var \Drupal\arch_payment\PaymentMethodInterface $payment_method */
      $payment_method = $this->paymentMethodManager->createInstance($paymentMethodId, []);
    }
    catch (\Exception $e) {
      $this->logger('Onepage Checkout')->error(
        'Failed to retrieve payment method instance. Reason: @reason',
        ['@reason' => $e->getMessage()]
      );

      $form_state->setRebuild();
      return;
    }

    $payment_fee_price = $payment_method->getPaymentFee($this->order);
    if ($payment_fee_price->getCurrencyId() !== $order_currency) {
      $payment_fee_price = $payment_fee_price->getExchangedPrice($order_currency);
    }
    $this->order->setPaymentFee($payment_fee_price, $payment_method->getPluginId());

    if (!empty($form_state->getValue('note'))) {
      $orderExtraData['note'] = $form_state->getValue('note');
    }

    if (!empty($orderExtraData)) {
      $this->order->set('data', $orderExtraData);
    }

    $billing_address = $this->buildBillingAddress($form_state);
    $shipping_address = NULL;
    $shipping_address_selector = $form_state->getValue('shipping_address_selector');

    if ($shipping_address_selector == 'sameas') {
      $shipping_address = clone $billing_address;
    }
    elseif ($shipping_address_selector == 'new_shipping') {
      $shipping_address = $this->buildShippingAddress($form_state);
    }
    elseif (
      $this->hasAddressbook()
      && $shipping_address_selector == 'choose_address'
    ) {
      $shipping_address = $this->loadSelectedShippingAddress($form_state);
    }

    $this->order->setBillingAddress($billing_address);
    if ($shipping_address) {
      $this->order->setShippingAddress($shipping_address);
    }

    if (
      ($shipping_method = $this->order->getShippingMethod())
      && $shipping_method instanceof ShippingMethodInterface
    ) {
      $shipping_price = $shipping_method->getShippingPrice($this->order);
      if ($shipping_price->getCurrencyId() !== $order_currency) {
        $shipping_price = $shipping_price->getExchangedPrice($order_currency);
      }
      $this->order->setShippingPrice($shipping_price, $shipping_method->getPluginId());
    }

    try {
      // Update total to have shipping & payment fees applied.
      $this->order->updateTotal();

      $this->order->save();

      $form_state->setRedirect($payment_method->getCallbackRoute(), ['order' => $this->order->id()]);
    }
    catch (\Exception $e) {
      $this->logger('Onepage Checkout')->error('Failed to save order. Reason: ' . $e->getMessage());
      return;
    }
  }

  /**
   * Gets the billing form fields.
   *
   * @param null|array $_billing
   *   Billing data from storage.
   *
   * @return array
   *   Billing form fields.
   */
  private function billingForm($_billing = NULL) {
    $billing = [
      '#type' => 'fieldset',
      '#title' => $this->t('Personal Data', [], ['context' => 'arch_onepage']),
      '#collapsed' => FALSE,
      '#collapsible' => FALSE,
      '#attributes' => [
        'class' => [
          'checkout-fieldset',
          'checkout-billing-info',
          'billing-info',
        ],
      ],
      '#suffix' => $this->billingFormCollapsedView(),
    ];

    if ($this->currentUser->isAnonymous()) {
      $billing['login'] = [
        '#type' => 'inline_template',
        '#template' => '<div class="checkout-login"><span>{{ text }}</span><a href="{{ button_url }}" class="login-button">{{ button_text }}</a></div>',
        '#context' => [
          'text' => $this->t('Already registered?', [], ['context' => 'arch_onepage']),
          'button_url' => '/user/login?destination=' . $this->destination->get(),
          'button_text' => $this->t('Please sign in.', [], ['context' => 'arch_onepage']),
        ],
      ];
    }

    $billing['email'] = [
      '#type' => 'textfield',
      '#placeholder' => $this->t('Email Address', [], ['context' => 'arch_onepage']),
      '#title' => $this->t('Email Address', [], ['context' => 'arch_onepage']),
      '#attributes' => [
        'data-field' => 'email',
      ],
      '#readonly' => $this->currentUser->isAuthenticated(),
      '#default_value' => $this->currentUser->isAuthenticated() ? $this->currentUser->getEmail() : '',
      '#required' => TRUE,
    ];

    foreach ($this->getAddressFields() as $field_name => $field_descriptor) {
      $billing[$field_name] = $field_descriptor;
      $billing[$field_name]['#attributes']['data-field'] = $field_name;
    }

    $billing['next_to_shipping'] = [
      '#type' => 'button',
      '#id' => 'btn-next-to-shipping',
      '#value' => $this->t('Next to Shipping', [], ['context' => 'arch_onepage']),
      '#weight' => 200,
    ];

    return $billing;
  }

  /**
   * Summarized view of billing form field data.
   *
   * @return mixed
   *   Rendereable array of summarized view of billing form field data.
   */
  private function billingFormCollapsedView() {
    $output = [
      '#type' => 'container',
      '#attributes' => [
        'id' => 'preview-billing-infos',
        'class' => [
          'row',
          'hidden',
          'section-preview',
          'clearfix',
        ],
      ],
      'contact' => [
        '#type' => 'container',
        '#attributes' => [
          'class' => [
            'col-md-4',
            'preview-billing-contact',
          ],
        ],
        'label' => [
          '#markup' => '<h3>' . $this->t('Contact', [], ['context' => 'arch_onepage']) . '</h3>',
        ],
        'name' => [
          '#type' => 'container',
          'firstname' => ['#markup' => '<span id="preview-billing-firstname"></span>'],
          'lastname' => ['#markup' => '<span id="preview-billing-lastname"></span>'],
        ],
        'email' => ['#markup' => '<div id="preview-billing-email"></div>'],
        'phone' => ['#markup' => '<div id="preview-billing-phone"></div>'],
      ],
      'billing' => [
        '#type' => 'container',
        '#attributes' => [
          'class' => [
            'col-md-4',
            'preview-billing-data',
          ],
        ],
        'label' => [
          '#markup' => '<h3>' . $this->t('Billing Info', [], ['context' => 'arch_onepage']) . '</h3>',
        ],
        'company' => ['#markup' => '<div id="preview-billing-company"></div>'],
        'postcity' => [
          '#type' => 'container',
          'country' => ['#markup' => '<span id="preview-billing-country"></span>'],
          'postcode' => ['#markup' => '<span id="preview-billing-postcode"></span>'],
          'city' => ['#markup' => '<span id="preview-billing-city"></span>'],
        ],
        'addresses' => [
          '#type' => 'container',
          'address' => ['#markup' => '<span id="preview-billing-address"></span>'],
          'address2' => ['#markup' => '<span id="preview-billing-address2"></span>'],
        ],
        'tax' => ['#markup' => '<div id="preview-billing-tax"></div>'],
      ],
    ];

    return $this->renderer->render($output);
  }

  /**
   * Gets the shipping form fields.
   *
   * @param null|array $_shipping
   *   Shipping data from storage.
   * @param null|array $form_billing
   *   Billing form.
   *
   * @return array
   *   Shipping form fields.
   *
   * @throws \Exception
   */
  private function shippingForm($_shipping = NULL, $form_billing = NULL) {
    $shipping = [
      '#type' => 'fieldset',
      '#title' => $this->t('Shipping', [], ['context' => 'arch_onepage']),
      '#collapsed' => TRUE,
      '#collapsible' => FALSE,
      '#attributes' => [
        'class' => [
          'checkout-fieldset',
          'checkout-shipping-info',
          'shipping-info',
        ],
      ],
      '#suffix' => $this->shippingFormCollapsedView(),
    ];

    $default_shipping_method = 'instore';
    $shipping_methods = $this->getShippingMethods();
    $shipping['shipping_methods'] = [
      '#type' => 'radios',
      '#title' => $this->t('Choose a shipping method', [], ['context' => 'arch_onepage']),
      '#options' => $shipping_methods,
      '#default_value' => $default_shipping_method,
      '#attributes' => [
        'class' => [
          'onepage-shipping-methods',
          'col-md-4',
        ],
        'data-field' => 'shipping-method',
        'required' => 'required',
      ],
      '#required' => TRUE,
      '#ajax' => [
        'event' => 'change',
        'callback' => [$this, 'changeShippingMethod'],
      ],
    ];

    $shipping['clearfix'] = [
      '#markup' => '<div class="clearfix"></div>',
    ];

    $shipping['shipping_address_selector'] = [
      '#type' => 'radios',
      '#title' => $this->t('Choose a shipping address', [], ['context' => 'arch_onepage']),
      '#title_display' => 'invisible',
      '#default_value' => 'sameas',
      '#attributes' => [
        'class' => [
          'shipping-address-selector',
        ],
        'data-field' => 'shipping-address-selector',
      ],
      '#options' => [
        'sameas' => $this->t('Same as billing info', [], ['context' => 'arch_onepage']),
        'new_shipping' => $this->t('Add new shipping address', [], ['context' => 'arch_onepage']),
      ],
      '#states' => [
        'invisible' => [
          ':input[name="shipping_methods"]' => ['value' => 'instore'],
        ],
      ],
    ];

    if ($this->hasAddressbook()) {
      $choose_address = $this->getAddressListOptions();
      if (!empty($choose_address)) {
        $shipping['shipping_address_selector']['#options']['choose_address'] = $this->t('Choose from address list', [], ['context' => 'arch_onepage']);
      }

      $shipping['new_shipping'] = [
        '#type' => 'fieldset',
        '#title' => $this->t('Create new shipping address', [], ['context' => 'arch_onepage']),
        '#attributes' => [
          'class' => [
            'shipping-new-address',
          ],
        ],
        '#states' => [
          'visible' => [
            ':input[name="shipping_address_selector"]' => ['value' => 'new_shipping'],
          ],
        ],
        'fields' => $this->newShippingAddressForm(),
      ];

      $shipping['choose_address'] = [
        '#type' => 'fieldset',
        '#title' => $this->t('Address list', [], ['context' => 'arch_onepage']),
        '#attributes' => [
          'class' => [
            'shipping-choose-from-list',
          ],
        ],
        '#states' => [
          'visible' => [
            ':input[name="shipping_address_selector"]' => ['value' => 'choose_address'],
          ],
        ],
        'choose_address' => [
          '#type' => 'select',
          '#options' => $choose_address,
          '#title' => $this->t('Address list', [], ['context' => 'arch_onepage']),
          '#title_display' => 'invisible',
        ],
      ];
    }

    $shipping['phone'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => [
          'shipping-phone-phone-number',
          'clearfix',
        ],
      ],
    ];

    $shipping['phone']['label'] = [
      '#type' => 'inline_template',
      '#template' => '<div class="form-section-subtitle">{{ label }}<span class="form-required">*</span></div>',
      '#context' => [
        'label' => $this->t('Phone Number', [], ['context' => 'arch_onepage']),
      ],
    ];

    $shipping['phone']['phone_prefix'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Phone Number Prefix', [], ['context' => 'arch_onepage']),
      '#title_display' => 'invisible',
      '#description' => $this->t('E.g.: +36', [], ['context' => 'arch_onepage']),
      '#required' => TRUE,
      '#maxlength' => 3,
    ];

    $shipping['phone']['phone_number'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Phone Number', [], ['context' => 'arch_onepage']),
      '#title_display' => 'invisible',
      '#description' => $this->t('E.g.: 30 123 4567', [], ['context' => 'arch_onepage']),
      '#required' => TRUE,
    ];

    $shipping['next_to_payment'] = [
      '#type' => 'button',
      '#id' => 'btn-next-to-payment',
      '#value' => $this->t('Next to Payment', [], ['context' => 'arch_onepage']),
    ];

    return $shipping;
  }

  /**
   * Summarized view of shipping form field data.
   *
   * @return \Drupal\Component\Render\MarkupInterface|string
   *   Rendereable array of summarized view of shipping form field data.
   *
   * @throws \Exception
   */
  private function shippingFormCollapsedView() {
    // @codingStandardsIgnoreStart
    $output = [
      '#type' => 'container',
      '#attributes' => [
        'id' => 'preview-shipping-infos',
        'class' => [
          'row',
          'hidden',
          'section-preview',
          'clearfix',
        ],
      ],
      'method' => [
        '#type' => 'container',
        '#attributes' => [
          'class' => [
            'col-md-4',
            'preview-shipping-contact',
          ],
        ],
        'label' => [
          '#markup' => '<h3>' . $this->t('Shipping Method', [], ['context' => 'arch_onepage']) . '</h3>',
        ],
        'method' => [
          '#type' => 'container',
          'method' => ['#markup' => '<span id="preview-shipping-method"></span>'],
          'instore' => ['#markup' => '<span id="preview-shipping-instore"></span>'],
        ],
      ],
      'shipping' => [
        '#type' => 'container',
        '#attributes' => [
          'class' => [
            'col-md-4',
            'preview-shipping-data',
          ],
        ],
        'label' => [
          '#markup' => '<h3>' . $this->t('Shipping Info', [], ['context' => 'arch_onepage']) . '</h3>',
        ],
        'sameas' => ['#markup' => '<div id="preview-shipping-sameas" class="hidden">' . $this->t('Same as billing info', [], ['context' => 'arch_onepage']) . '</div>'],
        'company' => ['#markup' => '<div id="preview-shipping-company"></div>'],
        'postcity' => [
          '#type' => 'container',
          'country' => ['#markup' => '<span id="preview-shipping-country"></span>'],
          'postcode' => ['#markup' => '<span id="preview-shipping-postcode"></span>'],
          'city' => ['#markup' => '<span id="preview-shipping-city"></span>'],
        ],
        'addresses' => [
          '#type' => 'container',
          'address' => ['#markup' => '<span id="preview-shipping-address"></span>'],
          'address2' => ['#markup' => '<span id="preview-shipping-address2"></span>'],
        ],
        'tax' => ['#markup' => '<div id="preview-shipping-tax"></div>'],
      ],
    ];
    // @codingStandardsIgnoreEnd

    return $this->renderer->render($output);
  }

  /**
   * Address fields subform.
   *
   * @return array
   *   Address fields subform.
   */
  private function getAddressFields() {
    $language = $this->languageManager->getCurrentLanguage()->getId();
    $firstname_weight = 10;

    if ($language == 'hu') {
      $firstname_weight = 25;
    }

    // @codingStandardsIgnoreStart
    return [
      'firstname' => [
        '#type' => 'textfield',
        '#placeholder' => $this->t('First name', [], ['context' => 'arch_onepage']),
        '#title' => $this->t('First name', [], ['context' => 'arch_onepage']),
        '#required' => TRUE,
        '#weight' => $firstname_weight,
      ],
      'lastname' => [
        '#type' => 'textfield',
        '#placeholder' => $this->t('Last name', [], ['context' => 'arch_onepage']),
        '#title' => $this->t('Last name', [], ['context' => 'arch_onepage']),
        '#required' => TRUE,
        '#weight' => 20,
      ],
      'company' => [
        '#type' => 'textfield',
        '#placeholder' => $this->t('Company (optional)', [], ['context' => 'arch_onepage']),
        '#title' => $this->t('Company', [], ['context' => 'arch_onepage']),
        '#required' => FALSE,
        '#weight' => 30,
      ],
      'tax_number' => [
        '#type' => 'textfield',
        '#placeholder' => $this->t('TAX Number', [], ['context' => 'arch_onepage']),
        '#title' => $this->t('TAX Number', [], ['context' => 'arch_onepage']) . '<span class="form-required">*</span>',
        '#required' => FALSE,
        '#attributes' => [
          'autocomplete' => 'off',
        ],
        '#states' => [
          'required' => [
            ':input[name="company"]' => ['filled' => TRUE],
          ],
          'visible' => [
            ':input[name="company"]' => ['filled' => TRUE],
          ],
          'invisible' => [
            ':input[name="company"]' => ['filled' => FALSE],
          ],
        ],
        '#weight' => 40,
      ],
      'address' => [
        '#type' => 'textfield',
        '#placeholder' => $this->t('Address', [], ['context' => 'arch_onepage']),
        '#title' => $this->t('Address', [], ['context' => 'arch_onepage']),
        '#description' => $this->t('Street name, house number', [], ['context' => 'arch_onepage']),
        '#required' => TRUE,
        '#weight' => 50,
      ],
      'address2' => [
        '#type' => 'textfield',
        '#placeholder' => $this->t('Address (optional)', [], ['context' => 'arch_onepage']),
        '#title' => $this->t('Address (optional)', [], ['context' => 'arch_onepage']),
        '#description' => $this->t('Building, unit, floor, door number', [], ['context' => 'arch_onepage']),
        '#required' => FALSE,
        '#weight' => 60,
      ],
      'postcode' => [
        '#type' => 'textfield',
        '#placeholder' => $this->t('Postcode', [], ['context' => 'arch_onepage']),
        '#title' => $this->t('Postcode', [], ['context' => 'arch_onepage']),
        '#description' => $this->t('Type your post code here, e.g.: 1042', [], ['context' => 'arch_onepage']),
        '#required' => TRUE,
        '#weight' => 70,
      ],
      'country' => [
        '#type' => 'select',
        '#placeholder' => $this->t('Country', [], ['context' => 'arch_onepage']),
        '#title' => $this->t('Country', [], ['context' => 'arch_onepage']),
        '#options' => $this->getAvailableCountries(),
        '#default_value' => 'HU',
        '#required' => TRUE,
        '#weight' => 80,
      ],
      'city' => [
        '#type' => 'textfield',
        '#placeholder' => $this->t('City', [], ['context' => 'arch_onepage']),
        '#title' => $this->t('City', [], ['context' => 'arch_onepage']),
        '#required' => TRUE,
        '#weight' => 90,
      ],
    ];
    // @codingStandardsIgnoreEnd
  }

  /**
   * Payment subform.
   *
   * @return array
   *   Subform.
   */
  private function paymentForm() {
    $payment = [
      '#type' => 'fieldset',
      '#title' => $this->t('Payment', [], ['context' => 'arch_onepage']),
      '#collapsed' => TRUE,
      '#collapsible' => FALSE,
      '#attributes' => [
        'class' => [
          'checkout-fieldset',
          'checkout-payment-method',
          'payment-method',
        ],
      ],
    ];

    $payment['payment_method'] = [
      '#type' => 'radios',
      '#title' => $this->t('Choose a payment method', [], ['context' => 'arch_onepage']),
      '#options' => $this->getAvailablePaymentMethodsOptions(),
      '#required' => TRUE,
      '#attributes' => [
        'required' => 'required',
      ],
      '#ajax' => [
        'event' => 'change',
        'callback' => [$this, 'changePaymentMethod'],
        'wrapper' => 'onepage-checkout-form-wrapper',
      ],
    ];

    $payment['note'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Note', [], ['context' => 'arch_onepage']),
    ];

    return $payment;
  }

  /**
   * Get shipping methods for Onepage Checkout form.
   *
   * @return array
   *   Array of options with shipping methods.
   */
  private function getShippingMethods() {
    $shipping_methods = $this->shippingMethodManager->getAvailableShippingMethods($this->order);

    $methods = [];
    foreach ($shipping_methods as $shipping_method) {
      $methods[$shipping_method->getPluginId()] = $shipping_method->getLabel();
    }

    return $methods;
  }

  /**
   * Get a list of available countries to ship.
   *
   * @return array
   *   Array of countries.
   */
  private function getAvailableCountries() {
    // @todo These should comes from settings.
    return [
      'HU' => $this->t('Hungary'),
    ];
  }

  /**
   * Get payment methods for Onepage Checkout form.
   *
   * @return array
   *   Array of options with payment methods.
   */
  private function getAvailablePaymentMethods() {
    return $this->paymentMethodManager->getAvailablePaymentMethods($this->order);
  }

  /**
   * Get payment method options for Onepage Checkout form.
   *
   * @return array
   *   Array of options with payment methods.
   */
  private function getAvailablePaymentMethodsOptions() {
    $methods = [];
    foreach ($this->getAvailablePaymentMethods() as $method) {
      /** @var \Drupal\arch_payment\PaymentMethodInterface $method */
      $methods[$method->getPluginId()] = $method->getLabel();
    }

    return $methods;
  }

  /**
   * Change payment method AJAX callback.
   *
   * @param array $form
   *   Form data.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state data.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   Response to payment method change.
   */
  public function changePaymentMethod(array &$form, FormStateInterface $form_state) {
    // @todo Should we check here for the available shipping methods after we
    // have changed the payment method.
    $response = new AjaxResponse();
    $response->addCommand(
      new InvokeCommand('body', 'trigger', ['checkout.onepage.phaseRecheck'])
    );

    return $response;
  }

  /**
   * Change shipping method AJAX callback.
   *
   * @param array $form
   *   Form data.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state data.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   Response to shipping method change.
   */
  public function changeShippingMethod(array &$form, FormStateInterface $form_state) {
    $method = $form_state->getValue('shipping_methods');
    $method = Xss::filter($method);
    $shipping_method = $this->shippingMethodManager->getShippingMethod($method);
    $this->order->setShippingMethod($shipping_method);

    $response = new AjaxResponse();

    $this->moduleHandler->invokeAll(
      'shipping_method_changed',
      [
        $shipping_method,
        $response,
        $this->cart,
      ]
    );

    return $response;
  }

  /**
   * Create user from Personal Data.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state object.
   *
   * @return bool|\Drupal\user\Entity\User
   *   User object, or FALSE when error occured.
   */
  private function createUser(FormStateInterface $form_state) {
    // @todo TEST this.
    /** @var \Drupal\user\Entity\User $user */
    $user = User::create();
    $user->setEmail($form_state->getValue('email'));

    $username = substr($form_state->getValue('email'), 0, strpos($form_state->getValue('email'), '@'));
    $username = str_replace('+', '_', $username);
    $_username = $username . '_' . mt_rand(999, 99999);
    while (empty($this->userStorage->loadByProperties(['name' => $_username]))) {
      $_username = $username . '_' . mt_rand(999, 99999);
    }
    $user->setUsername($_username);

    $rnd = new Random();
    $pwd = $rnd->name(12);
    $user->setPassword($pwd);

    $user->activate();
    $user->enforceIsNew();
    try {
      $user->save();

      // Login in user programmatically.
      // @codingStandardsIgnoreStart
      user_login_finalize($user);
      // @codingStandardsIgnoreEnd

      // Send email to user about details.
      // @codingStandardsIgnoreStart
      _user_mail_notify('register_no_approval_required', $user);
      // @codingStandardsIgnoreEnd

      return $user;
    }
    catch (\Exception $e) {
      return FALSE;
    }
  }

  /**
   * A new shipping address form fields.
   *
   * @return array
   *   Form fields.
   */
  private function newShippingAddressForm() {
    $exclude = ['tax_number'];
    $addressform = [];
    foreach ($this->getAddressFields() as $field_name => $field_descriptor) {
      if (in_array($field_name, $exclude)) {
        continue;
      }

      $addressform['shipping_' . $field_name] = $field_descriptor;
      $addressform['shipping_' . $field_name]['#required'] = FALSE;
      $addressform['shipping_' . $field_name]['#attributes']['data-field'] = 'shipping-' . $field_name;

      if (isset($addressform['shipping_' . $field_name]['#required'])) {
        $addressform['shipping_' . $field_name]['#states'] = [
          'required' => [
            ':input[name="shipping_address_selector"]' => ['value' => 'new_shipping'],
          ],
        ];
      }
    }

    return $addressform;
  }

  /**
   * Collect addresses from modules to build a select options.
   *
   * @return array
   *   Select options array.
   */
  private function getAddressListOptions() {
    // @todo Rewrite to use address service to get user's addresses.
    $addresses = $this->moduleHandler->invokeAll('commerce_shipping_addresses');
    $this->moduleHandler->alter('commerce_shipping_addresses', $addresses);

    $options = [];
    foreach ($addresses as $id => $address) {
      $options[$id] = $address;
    }

    return $options;
  }

  /**
   * Build billing address data.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state.
   *
   * @return \Drupal\arch_order\OrderAddressDataInterface
   *   Order address instance.
   */
  protected function buildBillingAddress(FormStateInterface $form_state) {
    $phone = $form_state->getValue('phone_prefix') . $form_state->getValue('phone_number');
    $data = [
      'country_code' => $form_state->getValue('country'),
      'locality' => $form_state->getValue('city'),
      'postal_code' => $form_state->getValue('postcode'),
      'address_line1' => $form_state->getValue('address'),
      'address_line3' => $form_state->getValue('address2'),

      'organization' => $form_state->getValue('company'),
      'given_name' => $form_state->getValue('firstname'),
      'family_name' => $form_state->getValue('lastname'),

      'tax_id' => $form_state->getValue('tax_number'),
      'phone' => $phone,
    ];
    return new OrderAddressData($data);
  }

  /**
   * Build shipping address data.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state.
   *
   * @return \Drupal\arch_order\OrderAddressDataInterface
   *   Order address instance.
   */
  protected function buildShippingAddress(FormStateInterface $form_state) {
    $phone = $form_state->getValue('phone_prefix') . $form_state->getValue('phone_number');
    $data = [
      'country_code' => $form_state->getValue('shipping_country'),
      'locality' => $form_state->getValue('shipping_city'),
      'postal_code' => $form_state->getValue('shipping_postcode'),
      'address_line1' => $form_state->getValue('shipping_address'),
      'address_line2' => $form_state->getValue('shipping_address2'),

      'organization' => $form_state->getValue('shipping_company'),
      'given_name' => $form_state->getValue('shipping_firstname'),
      'family_name' => $form_state->getValue('shipping_lastname'),

      'tax_id' => $form_state->getValue('tax_number'),
      'phone' => $phone,
    ];
    return new OrderAddressData($data);
  }

  /**
   * Get selected shipping address as OrderAddressDataInterface.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state.
   *
   * @return \Drupal\arch_order\OrderAddressDataInterface|null
   *   OrderAddressDataInterface instance or NULL on failure.
   */
  protected function loadSelectedShippingAddress(FormStateInterface $form_state) {
    if (!$this->hasAddressbook()) {
      return NULL;
    }

    $selection = $form_state->getValue('choose_address');
    [$entity_type, $entity_id] = explode(':', $selection);
    if (
      $entity_type !== 'addressbookitem'
      || empty($entity_id)
    ) {
      return NULL;
    }

    /** @var \Drupal\arch_addressbook\AddressbookitemInterface[] $addressbookitems */
    $addressbookitems = $this->getAddressbookItemStorage()->loadByProperties([
      'user_id' => $this->order->getOwnerId(),
      'id' => $entity_id,
    ]);

    if (empty($addressbookitems)) {
      return NULL;
    }

    $addressbookitem = current($addressbookitems);
    return $addressbookitem->toOrderAddress();
  }

  /**
   * Check if addressbook feature is available for customers.
   *
   * @return bool
   *   Returns TRUE if addressbook feature is available.
   */
  protected function hasAddressbook() {
    return $this->moduleHandler->moduleExists('arch_addressbook');
  }

  /**
   * Get addressbook item storage.
   *
   * @return \Drupal\Core\Entity\EntityStorageInterface
   *   Storage.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getAddressbookItemStorage() {
    return $this->entityTypeManager->getStorage('addressbookitem');
  }

}

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

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