Drupal 8: Change Commerce Product Variation URL to SKU
To pull this off, we need to write a custom module.
Let's name our module commerce_variation_sku.
In our commerce_variation_sku.module file, we need implement the hook hook_entity_type_build to change the entity class and entity storage class to our custom classes.
/**
* Implements hook_entity_type_build().
*/
function commerce_variation_sku_entity_type_build(array &$entity_types) {
if (isset($entity_types['commerce_product_variation'])) {
$entity_types['commerce_product_variation']->setClass('Drupal\commerce_variation_sku\Entity\ProductVariation');
$entity_types['commerce_product_variation']->setStorageClass('Drupal\commerce_variation_sku\ProductVariationStorage');
}
}
Next, we create our custom entity class that will extend the original entity class.
<?php
namespace Drupal\commerce_variation_sku\Entity;
use Drupal\Core\Url;
use Drupal\commerce_product\Entity\ProductVariation as ProductVariationBase;
class ProductVariation extends ProductVariationBase {
/**
* {@inheritdoc}
*/
public function toUrl($rel = 'canonical', array $options = []) {
// Product variation URLs depend on the parent product.
if (!$this->getProductId()) {
// RouteNotFoundException tells EntityBase::uriRelationships()
// to skip this product variation's link relationships.
throw new RouteNotFoundException();
}
// StringFormatter assumes 'revision' is always a valid link template.
if (in_array($rel, ['canonical', 'revision'])) {
$route_name = 'entity.commerce_product.canonical';
$route_parameters = [
'commerce_product' => $this->getProductId(),
];
$options += [
'query' => [
'sku' => $this->getSku(),
],
'entity_type' => 'commerce_product',
'entity' => $this->getProduct(),
// Display links by default based on the current language.
'language' => $this->language(),
];
return new Url($route_name, $route_parameters, $options);
}
else {
return parent::toUrl($rel, $options);
}
}
}
Then we create our custom entity storage class that again extends the original entity storage class.
<?php
namespace Drupal\commerce_variation_sku;
use Drupal\commerce_product\Entity\ProductInterface;
use Drupal\commerce_product\ProductVariationStorage as ProductVariationStorageBase;
/**
* Defines the product variation storage.
*/
class ProductVariationStorage extends ProductVariationStorageBase {
/**
* {@inheritdoc}
*/
public function loadFromContext(ProductInterface $product) {
$current_request = $this->requestStack->getCurrentRequest();
if ($sku = $current_request->query->get('sku')) {
$variation = $this->loadBySku($sku);
if (in_array($variation->id(), $product->getVariationIds())) {
/** @var \Drupal\commerce_product\Entity\ProductVariationInterface $variation */
if ($variation->isPublished() && $variation->access('view')) {
return $variation;
}
}
}
return $product->getDefaultVariation();
}
}
Last step is to clear your cache and you're good to go 😀