定制 treehousetim/shopcart 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

treehousetim/shopcart

Composer 安装命令:

composer require treehousetim/shopcart

包简介

A simple, zero-dependency, zero UI, Shopping Cart library.

README 文档

README

Shopping Cart Core Functionality PHP Library

Installing

composer require treehousetim/shopcart

Requirements

shopCart has no third-party package dependencies — installing it pulls in nothing but the library itself.

It does require PHP 7 or 8 and the bcmath extension, which the library uses for exact decimal money math (amounts are handled as strings, never floats). bcmath ships with PHP and is enabled by default on most builds; if yours lacks it, install/enable ext-bcmath (e.g. apt-get install php-bcmath, or the --enable-bcmath build flag).

Interfaces and Abstract Classes

In order to use the shopCart, you will need to instantiate both a treehousetim\shopCart\cart and a treehousetim\shopCart\catalog object.

The constructor for the cart requires a catalog object be passed. In order for a catalog object to function, it requires a product loader be implemented that implements catalogLoaderInterface

use \treehousetim\shopCart\catalog;
use \treehousetim\shopCart\cart;

$catalog = ( new catalog() )
	->setProductLoader( new \application\cart\myProductLoader() )
	->populate();

$cart = new cart( $catalog );
$cart->setTotalTypeLoader( (new \application\cart\myCatalogTotalTypeLoader()))
	->setFormatter( new \application\cart\myProductAmountFormatter() )
	->setStorageHandler( (new \treehousetim\shopCart\cartStorageSession() ) )
	->load();

catalogLoaderInterface

A catalog object is used to load products into the product catalog.

interface catalogLoaderInterface
{
	public function hasProducts() : bool;
	public function nextProduct() : bool;
	public function getProduct() : product;
}

cartStorageInterface

You can use a lot of different storage options for a cart. treehousetim\shopCart provides an implementation for session storage.

After creating your implementation of this interface, you must supply it to your cart using $cart->setStorageHandler( $storageHandler );

interface cartStorageInterface
{
	public function loadCart( cart $cart ) : cartStorageInterface;
	public function emptyCart( cart $cart ) : cartStorageInterface;

	public function saveItems( array $items ) : cartStorageInterface;
	public function saveData( array $data ) : cartStorageInterface;

	// used to clean up after all storage is complete
	public function finalize( cart $cart ) : cartStorageInterface;
}

catalogTotalTypeLoaderInterface

You must implement a class that conforms to this interface to support different types of product totals. Typical e-commerce shop carts will probably only need a single catalogTotalType for price, but there are other businesses that need totals of different product properties.

interface catalogTotalTypeLoaderInterface
{
	public function nextType() : bool;
	public function getType() : catalogTotalType;
	public function resetType();
}

Here is a sample catalogTotalTypeLoaderInterface implementation for both price and some special case of "points"

<?php namespace application\libraries\cart;

use treehousetim\shopCart\catalogTotalTypeLoaderInterface;
use treehousetim\shopCart\catalogTotalType;

class totalTypeLoader implements catalogTotalTypeLoaderInterface
{
	protected $types;

	public function __construct()
	{
		$this->types[] = (new catalogTotalType( catalogTotalType::tPRODUCT_PRICE ) );
		$this->types[] = (new catalogTotalType( catalogTotalType::tPRODUCT_FIELD ) )
			->setIdentifier( 1 )
			->setUnit( 'points' )
			->setLabel( 'Point' )
			->setProductField( 'productPoints' );
	}
	//------------------------------------------------------------------------
	public function nextType() : bool
	{
		next( $this->types );
		if( key( $this->types ) === null )
		{
			return false;
		}

		return true;
	}
	//------------------------------------------------------------------------
	public function getType() : catalogTotalType
	{
		return current( $this->types );
	}
	//------------------------------------------------------------------------
	public function resetType()
	{
		reset( $this->types );
	}
}

Product Variations

A product can have variations along arbitrary attributes (color, size, …) using productVariation. A variation wraps a parent product and falls back to the parent for anything you don't override — name, description, image, category, formatter, total type identifier, and price.

Every variation is itself a product: give it a unique id and add it to the catalog alongside its parent. Because cart items are keyed by product id and cartStorageSession re-resolves products from the catalog on load, this is all that's needed for variations to survive a save/load round trip.

use treehousetim\shopCart\productVariation;

$shirt = ( new myProduct() )
	->setId( 'shirt' )
	->setName( 'T-Shirt' )
	->setPrice( '20.00' );

$shirtXL = ( new productVariation( $shirt ) )
	->setAttribute( 'size', 'XL' )
	->setAttribute( 'color', 'blue' )
	->setId( 'shirt-xl-blue' )
	->setPrice( '24.50' );     // optional — omit to inherit the parent's price

$catalog->addProduct( $shirt )->addProduct( $shirtXL );

$shirtXL->getAttribute( 'size' );  // 'XL' (unknown names throw
                                   // Exception::noSuchAttributeErrorCode)
$shirtXL->getAttributes();         // ['size' => 'XL', 'color' => 'blue']

For carts with multiple total types, a variation can also override the per-unit amount of any catalogTotalType::tPRODUCT_FIELD dimension with setFieldAmount( $productField, $amount ). When no field amount is set for a dimension, getAmountForCatalogTotalType() delegates to the parent product.

Serialized Variations

productVariationSerialized models variations where each unit is a distinct physical item identified by a serial number. Rules enforced by the library:

  • Quantity is always exactly 1 per serial. cart::addProduct() with a quantity other than 1, or cart::updateItemQty() to anything other than 1, throws Exception::serializedQtyErrorCode.
  • Adding the same serial twice throws Exception::duplicateSerialErrorCode (an exception rather than a silent no-op, so the UI can tell the shopper the unit is already in the cart). The cart is left unchanged.
  • Id resolution: if you don't call setId(), the id is derived as parentId . ':' . serialNumber, so each unit is unique in the catalog and the session round trip works unchanged. Add every serialized unit to the catalog like any other product.

Each serialized unit can carry its own amount for each total type — its own price and its own per-unit amount for any tPRODUCT_FIELD dimension — so two serials of the same product can total differently in every dimension:

use treehousetim\shopCart\productVariationSerialized;

$unitA = ( new productVariationSerialized( $collectible ) )
	->setSerialNumber( 'SN-0001' )
	->setPrice( '102.75' )
	->setFieldAmount( 'points', '7.5' );

$unitB = ( new productVariationSerialized( $collectible ) )
	->setSerialNumber( 'SN-0002' )
	->setPrice( '99.10' )
	->setFieldAmount( 'points', '3.25' );

$catalog->addProduct( $unitA )->addProduct( $unitB );

$cart->addProduct( $unitA, 1 );    // qty must be exactly 1
$cart->addProduct( $unitB, 1 );
// cart::getAmountTotal()/getTotal() now sum the per-serial amounts

product::isSerialized() (false on every product except serialized variations) lets storage handlers and UIs detect these items generically.

totalFormatterInterface

interface totalFormatterInterface
{
	public function formatTotalType( string $value, catalogTotalType $type );
}

Testing

If you have cloned this repo, you can run the test suite. The library itself has no third-party dependencies; the tests use Pest, which is installed as a dev dependency by composer.

  1. composer install
  2. ./vendor/bin/pest

The suite lives under test/, with shared helpers in test/helpers.php (loaded via composer's autoload-dev) and reusable fixtures in test/fixture*.php. Note that Pest's runner requires PHP 8.2+; the library itself still supports PHP ^7.0 at runtime.

To measure code coverage, run with a coverage driver enabled (xdebug or pcov):

./vendor/bin/pest --coverage

The suite covers 100% of src/. If your PHP has no coverage driver, you can measure it in a throwaway container:

docker run --rm -v "$PWD":/app -w /app php:8.3-cli sh -c '
  apt-get update -qq && apt-get install -y -qq $PHPIZE_DEPS >/dev/null 2>&1
  docker-php-ext-install bcmath >/dev/null 2>&1
  pecl install pcov >/dev/null 2>&1 && docker-php-ext-enable pcov
  php vendor/bin/pest --coverage'

treehousetim/shopcart 适用场景与选型建议

treehousetim/shopcart 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.03k 次下载、GitHub Stars 达 1, 最近一次更新时间为 2020 年 04 月 30 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 treehousetim/shopcart 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 treehousetim/shopcart 我们能提供哪些服务?
定制开发 / 二次开发

基于 treehousetim/shopcart 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 1.03k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 11
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 3
  • Forks: 4
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2020-04-30