Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions modules/cart/src/Tests/AddToCartFormTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,60 @@ public function testRenderedAttributeElement() {
$this->assertText('Magenta (Rendered)');
}

/**
* Tests that the cart refreshes rendered variation fields.
*/
public function testRenderedVariationFields() {
/** @var \Drupal\commerce_product\Entity\ProductVariationTypeInterface $variation_type */
$variation_type = ProductVariationType::load($this->variation->bundle());

$color_attribute_values = $this->createAttributeSet($variation_type, 'color', [
'cyan' => 'Cyan',
'magenta' => 'Magenta',
], TRUE);

/** @var \Drupal\commerce_product\Entity\ProductVariationInterface $variation1 */
$variation1 = $this->createEntity('commerce_product_variation', [
'type' => 'default',
'sku' => 'RENDERED_VARIATION_TEST_CYAN',
'price' => [
'amount' => 999,
'currency_code' => 'USD',
],
'attribute_color' => $color_attribute_values['cyan'],
]);
/** @var \Drupal\commerce_product\Entity\ProductVariationInterface $variation2 */
$variation2 = $this->createEntity('commerce_product_variation', [
'type' => 'default',
'sku' => 'RENDERED_VARIATION_TEST_MAGENTA',
'price' => [
'amount' => 999,
'currency_code' => 'USD',
],
'attribute_color' => $color_attribute_values['magenta'],
]);
$product = $this->createEntity('commerce_product', [
'type' => 'default',
'title' => 'RENDERED_VARIATION_TEST',
'stores' => [$this->store],
'variations' => [$variation1, $variation2],
]);

$this->drupalGet($product->toUrl());

$this->assertText($variation1->getSku(), 'The SKU for the first variation is visible');

$response = $this->drupalPostAjaxForm(NULL, [
'purchased_entity[0][attributes][attribute_color]' => $color_attribute_values['magenta']->id(),
], 'purchased_entity[0][attributes][attribute_color]');

foreach ($response as $command) {
if ($command['command'] == 'insert' && $command['method'] == 'replaceWith' && $command['selector'] == '.product--variation-field--variation_sku__2') {
$this->assertTrue(strpos($command['data'], $variation2->getSku()) !== FALSE);
}
}
}

/**
* Creates an attribute field and set of attribute values.
*
Expand Down
39 changes: 39 additions & 0 deletions modules/product/commerce_product.module
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\commerce_product\Entity\ProductType;

/**
* Implements hook_entity_create().
Expand Down Expand Up @@ -70,6 +72,43 @@ function commerce_product_theme() {
];
}

/**
* Implements hook_ENTITY_TYPE_view().
*/
function commerce_product_commerce_product_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
/** @var \Drupal\commerce_product\Entity\ProductInterface $entity */
$product_type = ProductType::load($entity->bundle());
if ($product_type->shouldInjectVariationFields()) {
/** @var \Drupal\commerce_product\ProductVariationFieldRendererInterface $variation_field_renderer */
$variation_field_renderer = \Drupal::service('commerce_product.variation_field_renderer');

$rendered_fields = $variation_field_renderer->renderFields($entity->getDefaultVariation(), $view_mode);
foreach ($rendered_fields as $field_name => $rendered_field) {
$build['product_variation_' . $field_name] = $rendered_field;
}
}
}

/**
* Implements hook_ENTITY_TYPE_view().
*/
function commerce_product_commerce_product_variation_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
$stop = null;
}

/**
* Implements hook_entity_display_build_alter().
*/
function commerce_product_entity_display_build_alter(&$build, $context) {
if ($context['entity'] instanceof \Drupal\commerce_product\Entity\ProductVariationInterface) {
foreach ($build as $key => &$item) {
$field_class = \Drupal::service('commerce_product.variation_field_renderer')->getAjaxReplacementClass($key, $context['entity']->getProductId());
$item['#attributes']['class'][] = $field_class;
$item['#ajax_replace_class'] = $field_class;
}
}
}

/**
* Implements hook_theme_suggestions_commerce_product().
*/
Expand Down
4 changes: 4 additions & 0 deletions modules/product/commerce_product.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ services:
commerce_product.lazy_builders:
class: Drupal\commerce_product\ProductLazyBuilders
arguments: ['@entity_type.manager', '@entity.form_builder', '@commerce_product.line_item_type_map']

commerce_product.variation_field_renderer:
class: Drupal\commerce_product\ProductVariationFieldRenderer
arguments: ['@entity_type.manager', '@entity_field.manager']
3 changes: 3 additions & 0 deletions modules/product/config/schema/commerce_product.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ commerce_product.commerce_product_type.*:
variationType:
type: string
label: 'Variation type'
injectVariationFields:
type: boolean
label: 'Inject product variation fields into the rendered product'

commerce_product.commerce_product_variation_type.*:
type: config_entity
Expand Down
14 changes: 14 additions & 0 deletions modules/product/src/Entity/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,20 @@ public function setOwnerId($uid) {
return $this->set('uid', $uid);
}

/**
* {@inheritdoc}
*/
public function getDefaultVariation() {
foreach ($this->variations as $item) {
/** @var \Drupal\commerce_product\Entity\ProductVariationInterface $variation */
$variation = $item->entity;
// Return the first active variation.
if ($variation->isActive()) {
return $variation;
}
}
}

/**
* {@inheritdoc}
*/
Expand Down
8 changes: 8 additions & 0 deletions modules/product/src/Entity/ProductInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,12 @@ public function getStoreIds();
*/
public function setStoreIds(array $store_ids);

/**
* Gets the default product variation.
*
* @return \Drupal\commerce_product\Entity\ProductVariationInterface
* The default product variation.
*/
public function getDefaultVariation();

}
22 changes: 22 additions & 0 deletions modules/product/src/Entity/ProductType.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ class ProductType extends ConfigEntityBundleBase implements ProductTypeInterface
*/
protected $variationType;

/**
* Indicates if variation fields should be injected.
*
* @var bool
*/
protected $injectVariationFields = TRUE;

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -116,4 +123,19 @@ public function setVariationTypeId($variation_type_id) {
return $this;
}

/**
* {@inheritdoc}
*/
public function shouldInjectVariationFields() {
return $this->injectVariationFields;
}

/**
* {@inheritdoc}
*/
public function setInjectVariationFields($inject = TRUE) {
$this->injectVariationFields = (bool) $inject;
return $this;
}

}
19 changes: 19 additions & 0 deletions modules/product/src/Entity/ProductTypeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,23 @@ public function getVariationTypeId();
*/
public function setVariationTypeId($variation_type_id);

/**
* Gets whether variation fields should be injected into the rendered product.
*
* @return bool
* TRUE if the variation fields should be injected into the rendered
* product, FALSE otherwise.
*/
public function shouldInjectVariationFields();

/**
* Sets whether variation fields should be injected into the rendered product.
*
* @param bool $inject
* Whether variation fields should be injected into the rendered product.
*
* @return $this
*/
public function setInjectVariationFields($inject = TRUE);

}
13 changes: 13 additions & 0 deletions modules/product/src/Entity/ProductVariation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Drupal\commerce_product\Entity;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityStorageInterface;
Expand Down Expand Up @@ -259,6 +260,18 @@ protected function getAttributeFieldNames() {
return array_column($field_map, 'field_name');
}

/**
* {@inheritdoc}
*/
public function getCacheTagsToInvalidate() {
$tags = parent::getCacheTagsToInvalidate();
// Invalidate the variations view builder and product caches.
return Cache::mergeTags($tags, [
'commerce_product:' . $this->getProductId(),
'commerce_product_variation_view',
]);
}

/**
* {@inheritdoc}
*/
Expand Down
5 changes: 5 additions & 0 deletions modules/product/src/Form/ProductTypeForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ public function form(array $form, FormStateInterface $form_state) {
'#required' => TRUE,
'#disabled' => !$product_type->isNew(),
];
$form['injectVariationFields'] = [
'#type' => 'checkbox',
'#title' => $this->t('Inject product variation fields into the rendered product.'),
'#default_value' => $product_type->shouldInjectVariationFields(),
];
$form['product_status'] = [
'#type' => 'checkbox',
'#title' => t('Publish new products of this type by default.'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Drupal\commerce_product\Plugin\Field\FieldWidget;

use Drupal\commerce_product\Entity\ProductVariation;
use Drupal\commerce_product\Entity\ProductVariationInterface;
use Drupal\commerce_product\ProductAttributeFieldManagerInterface;
use Drupal\Component\Utility\Html;
Expand Down Expand Up @@ -143,11 +144,13 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
$parents = array_merge($element['#field_parents'], [$items->getName(), $delta]);
$user_input = (array) NestedArray::getValue($form_state->getUserInput(), $parents);
$selected_variation = $this->selectVariationFromUserInput($variations, $user_input);

$element['variation'] = [
'#type' => 'value',
'#value' => $selected_variation->id(),
];
// Set the selected variation in the form state for our AJAX callback.
$form_state->set('selected_variation', $selected_variation->id());

$element['attributes'] = [
'#type' => 'container',
'#attributes' => [
Expand Down Expand Up @@ -318,7 +321,19 @@ protected function getAttributeValues(array $variations, $field_name, callable $
* Ajax callback.
*/
public static function ajaxRefresh(array $form, FormStateInterface $form_state) {
return $form;
/** @var \Drupal\Core\Render\MainContent\MainContentRendererInterface $ajax_renderer */
$ajax_renderer = \Drupal::service('main_content_renderer.ajax');
$request = \Drupal::request();
$route_match = \Drupal::service('current_route_match');
/** @var \Drupal\Core\Ajax\AjaxResponse $response */
$response = $ajax_renderer->renderResponse($form, $request, $route_match);

$variation = ProductVariation::load($form_state->get('selected_variation'));
/** @var \Drupal\commerce_product\ProductVariationFieldRendererInterface $variation_field_renderer */
$variation_field_renderer = \Drupal::service('commerce_product.variation_field_renderer');
$variation_field_renderer->replaceRenderedFields($response, $variation, 'default');

return $response;
}

}
Loading