The ProductVariationStorageInterface offers a function loadEnabled(ProductInterface $product), that returns the enabled product variations for the given product. The implementing class filters by status and additionally dispatches an event to allow further filtering by contrib modules.

By doing the filter query, the original order of the varation, defined by its field delta, gets dismissed. The DB query returns the items in its natural order (which equals the entity id ascending normally), and so does the function. This is unexpected behaviour and leads to strange, inexplicable results, when you resort the variation references and save the product, but still see the same variation selected per default, when you view the product page.

Comments

agoradesign created an issue. See original summary.

agoradesign’s picture

Assigned: Unassigned » agoradesign

I'll assign this to me, as I'm currently writing test + patch

agoradesign’s picture

Assigned: agoradesign » Unassigned
Status: Active » Needs review
StatusFileSize
new1.46 KB
new2.49 KB
bojanz’s picture

Great work! I'll commit after dinner.

agoradesign’s picture

Thanks - enjoy your meal! :-)

agoradesign’s picture

@bojanz: hmmm apparently you had a great romantic dinner with the testbot yesterday! :DDD

bojanz’s picture

Status: Needs review » Needs work

@agoradesign
I feel asleep :)
There is no testbot here, DrupalCI doesn't support Composer, that's why we use GitHub and Travis.

I gave this another review:

use Drupal\commerce_product\Entity\Product;
use Drupal\commerce_product\Entity\ProductVariation;

Unused use statements.

class ProductVariationStorageTest extends ProductTestBase {

Could we make this a kernel test instead of a web test? We're not testing any UIs, and the kernel tests are much faster.
See the ProductAttributeFieldManagerTest test as an example.

agoradesign’s picture

I didn't know that about DrupalCI -> daily lesson learned :)

Good points. Will do the change later this day!

agoradesign’s picture

Before I'll bang my head into the wall, I'll go home now and watch The Simpsons on tv ;)

I tried to write a kernel test instead, but kept failing...

My testLoadEnabled() function would fail silently without any test results, if I'd try to save the product entity that I create. But here I can "cheat", as I don't need to save product at all becuase ProductVariationStorage::loadEnabled will only fetch the attributes from the product and do the filtering stuff afterwards.

The remaining problem is, that the call to ProductVariationStorage::loadEnabled returns an empty result. So it seems, my variations didn't get saved at all. Given the API documentation and ProductAttributeFieldManagerTest example, kernel tests do have access to the database, so no need to mock queries, etc. But there must be still something missing here... Here's my code so far:

<?php

namespace Drupal\commerce_product\Tests;

use Drupal\commerce_product\Entity\Product;
use Drupal\commerce_product\Entity\ProductType;
use Drupal\commerce_product\Entity\ProductVariation;
use Drupal\commerce_product\Entity\ProductVariationType;
use Drupal\KernelTests\KernelTestBase;

/**
 * Tests ProductVariationStorageInterface functionality.
 *
 * @group commerce
 */
class ProductVariationStorageTest extends KernelTestBase {

  /**
   * The product variation storage.
   *
   * @var \Drupal\commerce_product\ProductVariationStorageInterface
   */
  protected $variationStorage;

  /**
   * Modules to enable.
   *
   * @var array
   */
  public static $modules = ['system', 'field', 'options', 'user', 'path', 'text',
    'entity', 'commerce', 'commerce_price', 'commerce_store', 'commerce_product'];

  /**
   * {@inheritdoc}
   */
  protected function setUp() {
    parent::setUp();

    $this->installSchema('system', 'router');
    $this->installEntitySchema('user');
    $this->installEntitySchema('commerce_product_variation');
    $this->installEntitySchema('commerce_product_variation_type');
    $this->installEntitySchema('commerce_product');
    $this->installEntitySchema('commerce_product_type');

    $this->variationStorage = $this->container->get('entity_type.manager')->getStorage('commerce_product_variation');

    $variation_type = ProductVariationType::create([
      'id' => 'default',
      'label' => 'Default',
    ]);
    $variation_type->save();

    $product_type = ProductType::create([
      'id' => 'default',
      'label' => 'Default',
      'variationType' => 'default',
    ]);
    $product_type->save();
  }

  /**
   * Tests loadEnabled() function.
   */
  function testLoadEnabled() {
    $variations = [];
    for ($i = 1; $i <= 3; $i++) {
      $variation = ProductVariation::create([
        'type' => 'default',
        'sku' => strtolower($this->randomMachineName()),
        'status' => $i % 2,
      ]);
      $variation->save();
      $variations[] = $variation;
    }
    $variations = array_reverse($variations);
    $product = Product::create([
      'type' => 'default',
      'variations' => $variations,
    ]);

    $variationsFiltered = $this->variationStorage->loadEnabled($product);
    $this->assertEquals(2, count($variationsFiltered), '2 out of 3 variations are enabled');
    $this->assertEquals(reset($variations)->getSku(), reset($variationsFiltered)->getSku(), 'The sort order of the variations remains the same');
  }
}
agoradesign’s picture

It seems, I'm making progress now... hopefully can add the new patch soon.. Ok, still need more time, but at least I know, what's missing and why the tests fail

agoradesign’s picture

StatusFileSize
new3.54 KB

So here's the updated patch. Now the test extends \Drupal\system\Tests\Entity\EntityUnitTestBase, which may be changed later to \Drupal\KernelTests\Core\Entity\EntityKernelTestBase, once Commerce is compatible with 8.1.x. I've also moved the class into the Drupal\Tests\commerce_product\Kernel namespace

agoradesign’s picture

Status: Needs work » Needs review

  • bojanz committed d3e85fb on 8.x-2.x authored by agoradesign
    Issue #2691591 by agoradesign, bojanz: ProductVariationStorage::...
bojanz’s picture

Status: Needs review » Fixed

Updated the test to use EntityKernelTestBase (since we now require Drupal 8.1) and committed. Thanks!

agoradesign’s picture

You're welcome :-)

If I was nitpicky, I'd change the assertEqual() calls to assertEquals(), now that EntityKernelTestBase is used, since the first one is now deprecated :D

bojanz’s picture

agoradesign’s picture

Wow, you're really fast :)

rszrama’s picture

I loaned him my time machine. : D

Status: Fixed » Closed (fixed)

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