The way OrderTotalSummary and OrderItemTable formatters build their renderable array is not compatible with inserting them into views.

Here are the steps to reproduce:

  1. Make sure you have at least some orders already generated in your store
  2. Create a new view on commerce order
  3. Add "Total price" field to the view and specify "order total summary" as its formatter
  4. Add "Order items" field to the view and specify "order item table" as its formatter
  5. Observe nothing output in those 2 fields when the view is executed.

Both just do a simple (no-children) renderable array:
\Drupal\commerce_order\Plugin\Field\FieldFormatter\OrderTotalSummary::viewElements():

  public function viewElements(FieldItemListInterface $items, $langcode) {
    $order = $items->getEntity();
    return [
      '#theme' => 'commerce_order_total_summary',
      '#totals' => $this->orderTotalSummary->buildTotals($order),
    ];
  }

\Drupal\commerce_order\Plugin\Field\FieldFormatter\OrderItemTable::viewElements():

  public function viewElements(FieldItemListInterface $items, $langcode) {
    $order = $items->getEntity();
    return [
      '#type' => 'view',
      // @todo Allow the view to be configurable.
      '#name' => 'commerce_order_item_table',
      '#arguments' => [$order->id()],
      '#embed' => TRUE,
    ];
  }

Then the view runs the following code (in \Drupal\views\Plugin\views\field\EntityField::getItems()):

public function getItems(ResultRow $values) {
    if (!$this->displayHandler->useGroupBy()) {
      $build_list = $this->getEntityFieldRenderer()->render($values, $this);
    }
    else {
      // For grouped results we need to retrieve a massaged entity having
      // grouped field values to ensure that "grouped by" values, especially
      // those with multiple cardinality work properly. See
      // \Drupal\Tests\views\Kernel\QueryGroupByTest::testGroupByFieldWithCardinality.
      $display = [
        'type' => $this->options['type'],
        'settings' => $this->options['settings'],
        'label' => 'hidden',
      ];
      // Optional relationships may not provide an entity at all. So we can't
      // use createEntityForGroupBy() for those rows.
      if ($entity = $this->getEntity($values)) {
        $entity = $this->createEntityForGroupBy($entity, $values);
        // Some bundles might not have a specific field, in which case the faked
        // entity doesn't have it either.
        $build_list = isset($entity->{$this->definition['field_name']}) ? $entity->{$this->definition['field_name']}->view($display) : NULL;
      }
      else {
        $build_list = NULL;
      }
    }

  // ---------------------
  // --------------------- Here the $build_list is what the formatter has given us in its ::viewElements().
  // ---------------------

    if (!$build_list) {
      return [];
    }

    if ($this->options['field_api_classes']) {
      return [['rendered' => $this->renderer->render($build_list)]];
    }

  // ---------------------
  // --------------------- The part below is the problem: here we expect the $build_list to be
  // --------------------- indexed by deltas whereas the 2 formatters do not index them
  // --------------------- that way.
  // ---------------------

    // Render using the formatted data itself.
    $items = [];
    // Each item is extracted and rendered separately, the top-level formatter
    // render array itself is never rendered, so we extract its bubbleable
    // metadata and add it to each child individually.
    $bubbleable = BubbleableMetadata::createFromRenderArray($build_list);
    foreach (Element::children($build_list) as $delta) {
      BubbleableMetadata::createFromRenderArray($build_list[$delta])
        ->merge($bubbleable)
        ->applyTo($build_list[$delta]);
      $items[$delta] = [
        'rendered' => $build_list[$delta],
        // Add the raw field items (for use in tokens).
        'raw' => $build_list['#items'][$delta],
      ];
    }

  // ---------------------
  // --------------------- Thus down here we wind up with empty $items.
  // ---------------------

    return $items;
  }
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

bucefal91 created an issue. See original summary.

bucefal91’s picture

Status: Active » Needs review
FileSize
2.01 KB

Looking at the docs to the ::viewElements() method (in \Drupal\Core\Field\FormatterInterface) we see:

  /**
   * Builds a renderable array for a field value.
   *
   * @param \Drupal\Core\Field\FieldItemListInterface $items
   *   The field values to be rendered.
   * @param string $langcode
   *   The language that should be used to render the field.
   *
   * @return array
   *   A renderable array for $items, as an array of child elements keyed by
   *   consecutive numeric indexes starting from 0.
   */
  public function viewElements(FieldItemListInterface $items, $langcode);

So the interface obliges its implementations to index the returned renderable array by deltas. That means the 2 formatters are the offending side in this views vs. formatters confrontation :)

I attach a patch that simply wraps the existing 2 formatters into this delta notion that is expected from them.

bucefal91’s picture

FileSize
34.19 KB

I am not sure why it didn't want to queue my patch for testing, but I ran tests locally (including the JS ones) and have the following results (attached as a txt file).

sumanthkumarc’s picture

@bucefal91 , the D.O issue queues are used for issue management and the github repo of commerce : https://github.com/drupalcommerce is used for PR. then the code is synced back. i guess there's some problme with drupal CI bot for commerce.

sumanthkumarc’s picture

follow issue here: https://www.drupal.org/node/2855587 for CI bot, if you can help

bucefal91’s picture

I see. Didn't know about that. Thanks for letting me know.

Here's the pull request https://github.com/drupalcommerce/commerce/pull/723 (I hope I did everything properly, it's my 1st pull request on git hub :) )

bojanz’s picture

Status: Needs review » Needs work

Left comments on the PR, don't see why we're introducing the if ($items->count() > 0) { check.

bojanz’s picture

Title: OrderTotalSummary & OrderItemTable formatters not compatible with views » OrderTotalSummary and OrderItemTable formatters can crash when used inside Views

This got forgotten for a long time. Wrapping up.

  • bojanz committed 9097999 on 8.x-2.x authored by bucefal91
    Issue #2871569 by bucefal91, bojanz: OrderTotalSummary and...
bojanz’s picture

Status: Needs work » Fixed

Sorry for the delay!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.