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');
}
}
