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,
];

 

对文章内容有疑问题的朋友可以加QQ群讨论:747120338

本书共11小节。


评论 (1)