Skip to content

Commit f4fd6e1

Browse files
Matt Bredenbojanz
authored andcommitted
Create a resolver API for picking a checkout flow (#533)
1 parent d8a1240 commit f4fd6e1

7 files changed

Lines changed: 245 additions & 14 deletions

modules/checkout/commerce_checkout.services.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
services:
2+
commerce_checkout.chain_checkout_flow_resolver:
3+
class: Drupal\commerce_checkout\Resolver\ChainCheckoutFlowResolver
4+
tags:
5+
- { name: service_collector, call: addResolver, tag: commerce_checkout.checkout_flow_resolver }
6+
7+
commerce_checkout.default_checkout_flow_resolver:
8+
class: Drupal\commerce_checkout\Resolver\DefaultCheckoutFlowResolver
9+
arguments: ['@entity_type.manager']
10+
tags:
11+
- { name: commerce_checkout.checkout_flow_resolver, priority: -100 }
12+
213
commerce_checkout.checkout_order_manager:
314
class: Drupal\commerce_checkout\CheckoutOrderManager
4-
arguments: ['@entity_type.manager']
15+
arguments: ['@commerce_checkout.chain_checkout_flow_resolver']
516

617
plugin.manager.commerce_checkout_flow:
718
class: Drupal\commerce_checkout\CheckoutFlowManager

modules/checkout/src/CheckoutOrderManager.php

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,37 @@
22

33
namespace Drupal\commerce_checkout;
44

5+
use Drupal\commerce_checkout\Resolver\ChainCheckoutFlowResolverInterface;
56
use Drupal\commerce_order\Entity\OrderInterface;
6-
use Drupal\Core\Entity\EntityTypeManagerInterface;
77

8+
/**
9+
* Manages checkout flows for orders.
10+
*/
811
class CheckoutOrderManager implements CheckoutOrderManagerInterface {
912

1013
/**
11-
* The entity type manager.
14+
* The chain checkout flow resolver.
1215
*
13-
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
16+
* @var \Drupal\commerce_checkout\Resolver\ChainCheckoutFlowResolverInterface
1417
*/
15-
protected $entityTypeManager;
18+
protected $chainCheckoutFlowResolver;
1619

1720
/**
1821
* Constructs a new CheckoutOrderManager object.
1922
*
20-
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
21-
* The entity type manager.
23+
* @param \Drupal\commerce_checkout\Resolver\ChainCheckoutFlowResolverInterface $chain_checkout_flow_resolver
24+
* The chain checkout flow resolver.
2225
*/
23-
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
24-
$this->entityTypeManager = $entity_type_manager;
26+
public function __construct(ChainCheckoutFlowResolverInterface $chain_checkout_flow_resolver) {
27+
$this->chainCheckoutFlowResolver = $chain_checkout_flow_resolver;
2528
}
2629

2730
/**
2831
* {@inheritdoc}
2932
*/
3033
public function getCheckoutFlow(OrderInterface $order) {
3134
if ($order->checkout_flow->isEmpty()) {
32-
/** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
33-
$order_type = $this->entityTypeManager->getStorage('commerce_order_type')->load($order->bundle());
34-
$checkout_flow = $order_type->getThirdPartySetting('commerce_checkout', 'checkout_flow', 'default');
35-
// @todo Allow other modules to add their own resolving logic.
36-
$order->checkout_flow->target_id = $checkout_flow;
35+
$order->checkout_flow = $this->chainCheckoutFlowResolver->resolve($order);
3736
$order->save();
3837
}
3938

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace Drupal\commerce_checkout\Resolver;
4+
5+
use Drupal\commerce_checkout\Entity\CheckoutFlowInterface;
6+
use Drupal\commerce_order\Entity\OrderInterface;
7+
8+
/**
9+
* Default implementation of the chain checkout flow resolver.
10+
*/
11+
class ChainCheckoutFlowResolver implements ChainCheckoutFlowResolverInterface {
12+
13+
/**
14+
* The resolvers.
15+
*
16+
* @var \Drupal\commerce_checkout\Resolver\CheckoutFlowResolverInterface[]
17+
*/
18+
protected $resolvers = [];
19+
20+
/**
21+
* Constructs a new ChainCheckoutFlowResolver object.
22+
*
23+
* @param \Drupal\commerce_checkout\Resolver\CheckoutFlowResolverInterface[] $resolvers
24+
* The resolvers.
25+
*/
26+
public function __construct(array $resolvers = []) {
27+
$this->resolvers = $resolvers;
28+
}
29+
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function addResolver(CheckoutFlowResolverInterface $resolver) {
34+
$this->resolvers[] = $resolver;
35+
}
36+
37+
/**
38+
* {@inheritdoc}
39+
*/
40+
public function getResolvers() {
41+
return $this->resolvers;
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function resolve(OrderInterface $order) {
48+
foreach ($this->resolvers as $resolver) {
49+
$result = $resolver->resolve($order);
50+
if ($result instanceof CheckoutFlowInterface) {
51+
return $result;
52+
}
53+
}
54+
55+
return NULL;
56+
}
57+
58+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Drupal\commerce_checkout\Resolver;
4+
5+
/**
6+
* Runs the added resolvers one by one until one of them returns the checkout flow.
7+
*
8+
* Each resolver in the chain can be another chain, which is why this interface
9+
* extends the checkout flow resolver one.
10+
*/
11+
interface ChainCheckoutFlowResolverInterface extends CheckoutFlowResolverInterface {
12+
13+
/**
14+
* Adds a resolver.
15+
*
16+
* @param \Drupal\commerce_checkout\Resolver\CheckoutFlowResolverInterface $resolver
17+
* The resolver.
18+
*/
19+
public function addResolver(CheckoutFlowResolverInterface $resolver);
20+
21+
/**
22+
* Gets all added resolvers.
23+
*
24+
* @return \Drupal\commerce_checkout\Resolver\CheckoutFlowResolverInterface[]
25+
* The resolvers.
26+
*/
27+
public function getResolvers();
28+
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Drupal\commerce_checkout\Resolver;
4+
5+
use Drupal\commerce_order\Entity\OrderInterface;
6+
7+
/**
8+
* Defines the interface for checkout flow resolvers.
9+
*/
10+
interface CheckoutFlowResolverInterface {
11+
12+
/**
13+
* Resolves the checkout flow.
14+
*
15+
* @param \Drupal\commerce_order\Entity\OrderInterface $order
16+
* The order that is being checked out.
17+
*
18+
* @return \Drupal\commerce_checkout\Entity\CheckoutFlowInterface
19+
* The checkout flow, if resolved. Otherwise NULL, indicating that
20+
* the next resolver in the chain should be called.
21+
*/
22+
public function resolve(OrderInterface $order);
23+
24+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Drupal\commerce_checkout\Resolver;
4+
5+
use Drupal\commerce_order\Entity\OrderInterface;
6+
use Drupal\Core\Entity\EntityTypeManagerInterface;
7+
8+
/**
9+
* Returns the order type's default checkout flow.
10+
*/
11+
class DefaultCheckoutFlowResolver implements CheckoutFlowResolverInterface {
12+
13+
/**
14+
* The entity type manager.
15+
*
16+
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
17+
*/
18+
protected $entityTypeManager;
19+
20+
/**
21+
* Constructs a new DefaultCheckoutFlowResolver object.
22+
*
23+
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
24+
* The entity type manager.
25+
*/
26+
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
27+
$this->entityTypeManager = $entity_type_manager;
28+
}
29+
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function resolve(OrderInterface $order) {
34+
/** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
35+
$order_type = $this->entityTypeManager->getStorage('commerce_order_type')->load($order->bundle());
36+
$checkout_flow_id = $order_type->getThirdPartySetting('commerce_checkout', 'checkout_flow', 'default');
37+
$checkout_flow = $this->entityTypeManager->getStorage('commerce_checkout_flow')->load($checkout_flow_id);
38+
return $checkout_flow;
39+
}
40+
41+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace Drupal\Tests\commerce_checkout\Kernel;
4+
5+
use Drupal\commerce_order\Entity\Order;
6+
use Drupal\Tests\commerce\Kernel\CommerceKernelTestBase;
7+
8+
/**
9+
* Tests the chain checkout flow resolver.
10+
*
11+
* @group commerce
12+
*/
13+
class ChainCheckoutFlowResolverTest extends CommerceKernelTestBase {
14+
15+
/**
16+
* Modules to enable.
17+
*
18+
* @var array
19+
*/
20+
public static $modules = [
21+
'path',
22+
'options',
23+
'entity',
24+
'entity_reference_revisions',
25+
'views',
26+
'address',
27+
'profile',
28+
'state_machine',
29+
'inline_entity_form',
30+
'commerce',
31+
'commerce_price',
32+
'commerce_store',
33+
'commerce_product',
34+
'commerce_order',
35+
'commerce_checkout',
36+
];
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
protected function setUp() {
42+
parent::setUp();
43+
$this->installEntitySchema('commerce_order');
44+
$this->installConfig('commerce_order');
45+
$this->installConfig('commerce_product');
46+
$this->installConfig('commerce_checkout');
47+
}
48+
49+
/**
50+
* Tests resolving the checkout flow.
51+
*/
52+
public function testCheckoutFlowResolution() {
53+
$user = $this->createUser(['mail' => $this->randomString() . '@example.com']);
54+
$order = Order::create([
55+
'type' => 'default',
56+
'mail' => $user->getEmail(),
57+
'uid' => $user->id(),
58+
'store_id' => $this->store->id(),
59+
]);
60+
$order->save();
61+
62+
$resolver = $this->container->get('commerce_checkout.chain_checkout_flow_resolver');
63+
/** @var \Drupal\commerce_checkout\Entity\CheckoutFlowInterface $checkout_flow */
64+
$checkout_flow = $resolver->resolve($order);
65+
66+
$this->assertEquals('default', $checkout_flow->id());
67+
}
68+
69+
}

0 commit comments

Comments
 (0)