You are here

public function ShippingInformation::buildPaneForm in Commerce Shipping 8.2

Builds the pane form.

Parameters

array $pane_form: The pane form, containing the following basic properties:

  • #parents: Identifies the position of the pane form in the overall parent form, and identifies the location where the field values are placed within $form_state->getValues().

\Drupal\Core\Form\FormStateInterface $form_state: The form state of the parent form.

array $complete_form: The complete form structure.

Overrides CheckoutPaneInterface::buildPaneForm

File

src/Plugin/Commerce/CheckoutPane/ShippingInformation.php, line 214

Class

ShippingInformation
Provides the shipping information pane.

Namespace

Drupal\commerce_shipping\Plugin\Commerce\CheckoutPane

Code

public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) {
  $store = $this->order
    ->getStore();
  $available_countries = [];
  foreach ($store
    ->get('shipping_countries') as $country_item) {
    $available_countries[] = $country_item->value;
  }

  /** @var \Drupal\commerce\Plugin\Commerce\InlineForm\EntityInlineFormInterface $inline_form */
  $inline_form = $this->inlineFormManager
    ->createInstance('customer_profile', [
    'profile_scope' => 'shipping',
    'available_countries' => $available_countries,
    'address_book_uid' => $this->order
      ->getCustomerId(),
    // Don't copy the profile to address book until the order is placed.
    'copy_on_save' => FALSE,
  ], $this
    ->getShippingProfile());

  // Prepare the form for ajax.
  // Not using Html::getUniqueId() on the wrapper ID to avoid #2675688.
  $pane_form['#wrapper_id'] = 'shipping-information-wrapper';
  $pane_form['#prefix'] = '<div id="' . $pane_form['#wrapper_id'] . '">';
  $pane_form['#suffix'] = '</div>';

  // Auto recalculation is enabled only when a shipping profile is required.
  $pane_form['#auto_recalculate'] = !empty($this->configuration['auto_recalculate']) && !empty($this->configuration['require_shipping_profile']);
  $pane_form['#after_build'][] = [
    static::class,
    'autoRecalculateProcess',
  ];
  $pane_form['shipping_profile'] = [
    '#parents' => array_merge($pane_form['#parents'], [
      'shipping_profile',
    ]),
    '#inline_form' => $inline_form,
  ];
  $pane_form['shipping_profile'] = $inline_form
    ->buildInlineForm($pane_form['shipping_profile'], $form_state);
  $triggering_element = $form_state
    ->getTriggeringElement();

  // The shipping_profile should always exist in form state (and not just
  // after "Recalculate shipping" is clicked).
  if (!$form_state
    ->has('shipping_profile') || isset($triggering_element['#parents']) && in_array('select_address', $triggering_element['#parents'])) {
    $form_state
      ->set('shipping_profile', $inline_form
      ->getEntity());
  }
  $class = get_class($this);

  // Ensure selecting a different address refreshes the entire form.
  if (isset($pane_form['shipping_profile']['select_address'])) {
    $pane_form['shipping_profile']['select_address']['#ajax'] = [
      'callback' => [
        $class,
        'ajaxRefreshForm',
      ],
      'element' => $pane_form['#parents'],
    ];

    // Selecting a different address should trigger a recalculation.
    $pane_form['shipping_profile']['select_address']['#recalculate'] = TRUE;
  }
  $pane_form['recalculate_shipping'] = [
    '#type' => 'button',
    '#value' => $this
      ->t('Recalculate shipping'),
    '#recalculate' => TRUE,
    '#ajax' => [
      'callback' => [
        $class,
        'ajaxRefreshForm',
      ],
      'element' => $pane_form['#parents'],
    ],
    // The calculation process only needs a valid shipping profile.
    '#limit_validation_errors' => [
      array_merge($pane_form['#parents'], [
        'shipping_profile',
      ]),
    ],
    '#after_build' => [
      [
        static::class,
        'clearValues',
      ],
    ],
  ];
  $pane_form['removed_shipments'] = [
    '#type' => 'value',
    '#value' => [],
  ];
  $pane_form['shipments'] = [
    '#type' => 'container',
  ];
  $shipping_profile = $form_state
    ->get('shipping_profile');
  $shipments = $this->order
    ->get('shipments')
    ->referencedEntities();
  $recalculate_shipping = $form_state
    ->get('recalculate_shipping');
  $can_calculate_rates = $this
    ->canCalculateRates($shipping_profile);

  // If the shipping recalculation is triggered, ensure the rates can
  // be recalculated (i.e a valid address is entered).
  if ($recalculate_shipping && !$can_calculate_rates) {
    $recalculate_shipping = FALSE;
    $shipments = [];
  }

  // Ensure the profile is saved with the latest address, it's necessary
  // to do that in case the profile isn't new, otherwise the shipping profile
  // referenced by the shipment won't reflect the updated address.
  if (!$shipping_profile
    ->isNew() && $shipping_profile
    ->hasTranslationChanges() && $can_calculate_rates) {
    $shipping_profile
      ->save();
    $inline_form
      ->setEntity($shipping_profile);
  }
  $force_packing = empty($shipments) && $can_calculate_rates;
  if ($recalculate_shipping || $force_packing) {

    // We're still relying on the packer manager for packing the order since
    // we don't want the shipments to be saved for performance reasons.
    // The shipments are saved on pane submission.
    list($shipments, $removed_shipments) = $this->packerManager
      ->packToShipments($this->order, $shipping_profile, $shipments);

    // Store the IDs of removed shipments for submitPaneForm().
    $pane_form['removed_shipments']['#value'] = array_map(function ($shipment) {

      /** @var \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment */
      return $shipment
        ->id();
    }, $removed_shipments);
  }
  $single_shipment = count($shipments) === 1;
  foreach ($shipments as $index => $shipment) {

    /** @var \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment */
    $pane_form['shipments'][$index] = [
      '#parents' => array_merge($pane_form['#parents'], [
        'shipments',
        $index,
      ]),
      '#array_parents' => array_merge($pane_form['#parents'], [
        'shipments',
        $index,
      ]),
      '#type' => $single_shipment ? 'container' : 'fieldset',
      '#title' => $shipment
        ->getTitle(),
    ];
    $form_display = EntityFormDisplay::collectRenderDisplay($shipment, 'checkout');
    $form_display
      ->removeComponent('shipping_profile');
    $form_display
      ->buildForm($shipment, $pane_form['shipments'][$index], $form_state);
    $pane_form['shipments'][$index]['#shipment'] = $shipment;
  }

  // Update the shipments and save the order if no rate was explicitly
  // selected, that usually occurs when changing addresses, this will ensure
  // the default rate is selected/applied.
  if (!$this
    ->hasRateSelected($pane_form, $form_state)) {
    array_map(function (ShipmentInterface $shipment) {
      if (!$shipment
        ->isNew()) {
        $shipment
          ->save();
      }
    }, $shipments);
    $this->order
      ->set('shipments', $shipments);
    $this->order
      ->save();
  }
  return $pane_form;
}