2.5 Commerce Cart 模块:提供购物车功能
2.5 Commerce Cart 模块:提供购物车功能
在 Commerce Cart
模块在 Commerce Order
模块的基础上实现了购物车功能。
购物车被设计为一种特殊的订单。
我们来看看该模块都做了些什么事情:
cart
字段
为commerce_order
实体类型添加了一个cart
字段,用于标识一个订单是购物车订单,默认值为true
。 这个比较简单,非常好理解,通过hook_entity_base_field_info()
这个钩子来实现。
值得注意的是,一般不需要直接去操作这个字段的值,当需要把一个购物车订单确认为最终订单时,可以使用commerce_cart.cart_provider
服务的 finalizeCart()
方法。
CartProvider
服务
也就是上文提到的 commerce_cart.cart_provider
服务,它有好几个用途,开发者可以方便创建购物车订单、加载购物车订单、把购物车订单转化为确认的订单。 可以简单地从它的接口定义中了解它所能做的事情:
<?php
namespace Drupal\commerce_cart;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_store\Entity\StoreInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Creates and loads carts for anonymous and authenticated users.
*
* @see \Drupal\commerce_cart\CartSessionInterface
*/
interface CartProviderInterface {
/**
* Creates a cart order for the given store and user.
*
* @param string $order_type
* The order type ID.
* @param \Drupal\commerce_store\Entity\StoreInterface $store
* The store. If empty, the current store is assumed.
* @param \Drupal\Core\Session\AccountInterface $account
* The user. If empty, the current user is assumed.
*
* @return \Drupal\commerce_order\Entity\OrderInterface
* The created cart order.
*
* @throws \Drupal\commerce_cart\Exception\DuplicateCartException
* When a cart with the given criteria already exists.
*/
public function createCart($order_type, StoreInterface $store = NULL, AccountInterface $account = NULL);
/**
* Finalizes the given cart order.
*
* Removes the cart flag from the order and saves it.
* If the user is anonymous, moves the cart ID from the
* active to the completed cart session.
*
* @param \Drupal\commerce_order\Entity\OrderInterface $cart
* The cart order.
* @param bool $save_cart
* Whether to immediately save the cart or not.
*/
public function finalizeCart(OrderInterface $cart, $save_cart = TRUE);
/**
* Gets the cart order for the given store and user.
*
* @param string $order_type
* The order type ID.
* @param \Drupal\commerce_store\Entity\StoreInterface $store
* The store. If empty, the current store is assumed.
* @param \Drupal\Core\Session\AccountInterface $account
* The user. If empty, the current user is assumed.
*
* @return \Drupal\commerce_order\Entity\OrderInterface|null
* The cart order, or NULL if none found.
*/
public function getCart($order_type, StoreInterface $store = NULL, AccountInterface $account = NULL);
/**
* Gets the cart order ID for the given store and user.
*
* @param string $order_type
* The order type ID.
* @param \Drupal\commerce_store\Entity\StoreInterface $store
* The store. If empty, the current store is assumed.
* @param \Drupal\Core\Session\AccountInterface $account
* The user. If empty, the current user is assumed.
*
* @return int|null
* The cart order ID, or NULL if none found.
*/
public function getCartId($order_type, StoreInterface $store = NULL, AccountInterface $account = NULL);
/**
* Gets all cart orders for the given user.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The user. If empty, the current user is assumed.
*
* @return \Drupal\commerce_order\Entity\OrderInterface[]
* A list of cart orders.
*/
public function getCarts(AccountInterface $account = NULL);
/**
* Gets all cart order ids for the given user.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The user. If empty, the current user is assumed.
*
* @return int[]
* A list of cart orders ids.
*/
public function getCartIds(AccountInterface $account = NULL);
/**
* Clears the static caches.
*/
public function clearCaches();
}
add_to_cart
表单
这个表单的作用非常简单,就是要在产品实体视图上显示一个加入购物车
按钮,并且让用户可以选择要放进购物车的产品规格。
但是它的实现有一点点绕,特别值得在这里说明一下。
这需要先从 Commerce Product
模块说起:
- 实现了一个名为
commerce_add_to_cart
的FieldFormatter,在显示commerce_product
实体的variations
字段时, 该FieldFormatter使用了一个名为commerce_product.lazy_builders:addToCartForm
的lazy_builder
来进行异步渲染。 - 在
commerce_product.lazy_builders:addToCartForm
中,读取了当前显示产品的默认规格,用其创建了一个订单项,也就是一个commerce_order_item
Entity。 - 这个 Entity 将传给一个表单对象,这个表单类型是从
commerce_order_item
实体类型的add_to_cart
操作读取的。 - 把这个表单对象显示到用户界面,就形成了一个
添加到购物车
表单。
commerce_order_item
实体类型的add_to_cart
操作,是可以自定义表单类的,因此,在 Commerce Cart
模块中,就做了一个表单类的实现:
- 实现了一个
\Drupal\commerce_cart\Form\AddToCartForm
表单 - 通过
hook_entity_type_build()
钩子,把\Drupal\commerce_cart\Form\AddToCartForm
绑定到add_to_cart
操作。
这样,Commerce Cart
模块就与 Commerce Product
模块的开放性设计结合起来了。
我们值得再继续探索一下 \Drupal\commerce_cart\Form\AddToCartForm
这个表单类, 它实际上是继承自 Drupal\Core\Entity\ContentEntityForm
的,它只是一个普通的 ContentEntityForm,只是它重写了表单提交逻辑,变成创建购物车订单,而不是保存Entity数据。
还有一个重要的功能,就是用户选择要添加到购物车的产品规格,它是一个 Ajax更新表单,它是怎么实现的呢? 实际上在Commerce Product
模块中还有一个名为 commerce_product_variation_attributes
的 FieldWidget, 当 \Drupal\commerce_cart\Form\AddToCartForm
这个表单用 add_to_cart
模式去编辑订单项时,只显示了该 FieldWidget 去编辑 purchased_entity
字段,在这个 FieldWidget中发生了一系列复杂的逻辑,包括前面说的Ajax更新。
commerce_cart
Block
实现了一个名为 commerce_cart
的Block,用于显示当前用户的购物车状态,显示购物车中的商品信息。 这个比较简单,请参考 Drupal\commerce_cart\Plugin\Block\CartBlock
。
购物车详情页
用路由控制器实现了一个购物车详情页。 这个也比较简单,请参考 Drupal\commerce_cart\Controller\CartController
与 views
模块的整合
commerce_cart
Block 和 购物车详情页,分别包含一个订单项列表。 我们会发现有两个view被定义,它们是 commerce_cart_block
和 commerce_cart_form
,分别对应commerce_cart
Block 和 购物车详情页 的订单项列表。
用以下的方法,可以直接把已定义的view添加到渲染数组:
<?php
$build = [
'#type' => 'view',
'#name' => $cart_view,
'#arguments' => [$cart_id],
'#embed' => TRUE,
];