ittweb / accelasearch-product-mapper
允许轻松集成AccelaSearch的产品管理。
Requires
- php: >=7.1.0
Requires (Dev)
- phpunit/phpunit: >=7
README
PHP产品数据映射器,用于轻松集成AccelaSearch。
概述
AccelaSearch部署了一个中间SQL类似数据库,称为collector,用于存储产品信息,然后用于配置和填充搜索引擎和结果。必须以适当的格式存储项目信息(有关详细信息,请参阅AccelaSearch - 自定义集成文档)。AccelaSearch - 产品映射器是一个PHP库,它公开了一个抽象数据类型(ADT),用于表示AccelaSearch支持的所有不同类型的产品,以及一组方便的数据映射器,用于将数据从/到不同格式转换,包括JSON、PHP字典和中间SQL格式。
AccelaSearch - 产品映射器允许以抽象、语法友好方式定义产品数据,并从/向收集器数据库发送/检索信息,以简化与第三方系统或CMS的集成,而无需了解底层收集器数据库模式。请注意,然而,产品映射器将操作整个、原子实体上的产品,并且不会意识到CMS指定的行为,这些行为可能会加快同步过程(请参阅限制部分以获取示例)。
要求
AccelaSearch - 产品映射器是自包含的,它需要一个最新的PHP版本来运行。
提供的单元测试可以使用PHPUnit 运行,并且可以使用PHPDocmentor 生成代码文档。
安装
建议通过Composer 进行安装
composer require ittweb/accelasearch-product-mapper
可以通过克隆或下载此存储库手动安装
git clone https://github.com/ittweb/accelasearch-product-mapper.git
wget https://github.com/ittweb/accelasearch-product-mapper/archive/master.zip
概述
在注册AccelaSearch后,系统将发布一个API密钥,例如my-api-key
,应保密并安全存储。API密钥可以用于实例化一个DataMapper\Api\Client
对象,该对象允许检索有关支持的CMS和收集器的信息。前者应用于创建一个Shop
实例,而后者应用于建立与收集器的SQL连接,该连接可用于存储有关商店和产品的信息。为了方便这些操作,AccelaSearch - 产品映射器提供了一些实用工具和外观,这些工具和外观是交互与收集器的首选方式。
检索信息
基本信息,例如支持的CMS列表和收集器凭据,可以通过使用DataMapper\Api\Client
以及相关数据映射器通过AccelaSearch的API系统访问。
use \AccelaSearch\ProductMapper\DataMapper\Api\Client; use \AccelaSearch\ProductMapper\DataMapper\Api\Cms as CmsMapper; use \AccelaSearch\ProductMapper\DataMapper\Api\Collector as CollectorMapper; $client = Client::fromApiKey("my-api-key"); $cms_mapper = new CmsMapper($client); $collector_mapper = new CollectorMapper($client); $cms_list = $cms_mapper->search(); $collector = $collector_mapper->read();
连接到收集器
一旦获得收集器对象(这很可能是通过DataMapper\Api\Collector
完成的),就可以使用它来建立与收集器数据库的连接
use \PDO; $dbh = new PDO( 'mysql:host=' . $collector->getHostName() . ';dbname=' . $collector->getDatabaseName(), $collector->getUsername(), $collector->getPassword(), [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ] );
管理商店
可以使用SQL连接实例化一个商店数据映射器,该映射器可以进一步用于将Shop
实例插入到收集器中
use \AccelaSearch\ProductMapper\Shop; use \AccelaSearch\ProductMapper\DataMapper\Sql\Shop as ShopMapper; // $cms_list = ... // $dbh = ... $shop_mapper = ShopMapper::fromConnection($dbh); $shop = new Shop("http://www.shop.com", "en", $cms_list[0]); $shop_mapper->create($shop);
插入时将创建一个唯一的商店标识符,并将其分配给$shop
。此标识符可用于从数据库检索商店并更新其信息或软删除它
// Shop has identifier 2 $shop = $shop_mapper->read(2); $shop->setUrl("http://www.new-url.com"); $shop_mapper->update($shop); // Soft-deletion $shop->setIsActive(false); $shop_mapper->update($shop);
注意:不应手动硬删除商店,AccelaSearch将定期扫描收集器并采取适当措施,最终删除软删除的商店。
管理项目
处理项目数据的首选方式是通过 CollectorFacade
,它可以自动处理 SQL 事务和回滚、关系特征的插入以及隐式处理插入与更新。CollectorFacade
需要一个 DataMapper\API\Client
实例和操作的商店标识符,它将自动检索所有必要的连接信息。
use \AccelaSearch\ProductMapper\DataMapper\Api\Client; use \AccelaSearch\ProductMapper\CollectorFacade; $client = Client::fromApiKey("my-api-key"); $collector = new CollectorFacade($client, 2);
CollectorFacade
提供了四种与产品交互的方法:load
、searchByExternalIdentifier
、save
和 delete
。后者将执行软删除,而 save
将根据收集器中是否存在该项目执行插入或更新。
// Retrieves item with identifier 42 $item = $collector->load(42); // Retrieves item having external identifier "ITM0001" $item = $collector->searchByExternalIdentifier("ITM0001"); // Updates item information $item->setUrl("http://new-shop-url.com/ITM0001"); $collector->save($item); // Soft deletion $collector->delete($item);
项目层级
AccelaSearch 支持九种不同类型的物品
- 横幅:带有 URL 和图片的横幅(桌面和移动端不同)
- 页面:通用的网页
- 分类页面:分类或收集器的网页
- 标准:标准产品
- 虚拟:Simple 的子类型,通常意味着无需发货
- 可下载:Virtual 的子类型,可以下载的项目
- 可配置:存在一组变体项的产品,例如不同颜色或尺寸的衬衫;变体通常表示为 Simple 项,尽管可以使用任何类型的项
- 捆绑:捆绑销售的项目
- 分组:类似于捆绑,用于区分的 CMS,如 Magento
每种类型的物品在主 \AccelaSearch\ProductMapper
下都由同名的类表示。每个类都实现了 ItemInterface
,而 Simple
、Virtual
、Downloadable
、Configurable
、Bundle
和 Grouped
也实现了 ProductInterface
,它增加了关于外部标识符、属于零个或多个分类、图像信息、自定义属性以及关于可用性和定价的信息(通过扩展 StockableInterface
和 SellableInterface
)。
尽管可以通过构造函数创建项目,但实现了 ProductInterface
的产品可以通过 ProductFactory
实例化。
use \AccelaSearch\ProductMapper\ProductFactory; $factory = new ProductFactory(); $simple = $factory->createSimple("http://myshop.com/SIMPLE0001", "SIMPLE0001"); $virtual = $factory->createVirtual("http://myshop.com/VIRTUAL0001", "VIRTUAL0001"); $downloadable = $factory->createDownloadable("http://myshop.com/DOWNLOADABLE0001", "DOWNLOADABLE0001"); $configurable = $factory->createConfigurable("http://myshop.com/CONF0001", "CONF0001"); $bundle = $factory->createBundle("http://myshop.com/BUNDLE0001", "BUNDLE0001"); $grouped = $factory->createVirtual("http://myshop.com/GROUP0001", "GROUP0001");
每个方法都以其创建的产品类型命名,并接受产品的 URL 及其外部标识符。每个产品都创建为带有空可用性、定价和图像信息,并且没有分类。
添加标准属性
每个物品都允许通过访问器设置 SKU 和 URL,而产品还允许设置外部标识符、分类、图像信息、库存可用性、定价和自定义属性。前者可以通过以下方式访问:
// $item = ... $item->setSku("ITM-003"); $item->setUrl("http://www.myshop.com/catalogue/itm-003"); echo $item->getSku() . " " . $item->getUrl(); // $product = ... $product->setExternalIdentifier("56"); echo $product->getExternalIdentifier();
以下部分将展示如何处理更复杂的标准信息。
添加分类
在分配给产品之前必须创建(或从收集器读取)分类
use \AccelaSearch\ProductMapper\Category; $parent_category = new Category("cat-0001", "Fashion", null); $category = new Category("cat-00075", "Woman", $parent_category); $category->setUrl("http://www.myshop/categories/75"); $item->addCategory($category); $item->removeCategory($category);
分类不由 CollectorFacade
显式处理,它在需要时会透明地插入或读取数据。相反,可以通过使用 Repository\Sql\Category
存储库或更低级别的数据映射器 DataMapper\Sql\Category
进行原始操作来持久化分类。
添加图像信息
关于产品图片的信息由 Image
类处理,该类允许为每个图像指定一个标签、一个 URL 和一个位置
use \AccelaSearch\ProductMapper\Image; $image_1 = new Image("main", "http://www.myshop.com/storage/images/001.jpeg", 1); $image_2 = new Image("over", "http://www.myshop.com/storage/images/002.jpeg", 3);
可以通过 addImage
方法将图像添加到项目中
use \AccelaSearch\ProductMapper\ProductFactory; $factory = new ProductFactory(); $item = $factory->createSimple("http://myshop.com/SIMPLE0001", "SIMPLE0001"); $item->addImage($image_1)->addImage($image_2);
添加可用性
可用性始终与仓库相关,可能是有限的或无限的。在使用之前必须创建(或从收集器读取)仓库,可以是虚拟的或物理的(其中已知纬度和经度)。通过 ProductFactory
创建产品时,将自动创建一个空的 Stock\Availability
,并且可以通过其访问器方法访问它
use \AccelaSearch\ProductMapper\Stock\Warehouse\Virtual as VirtualWarehouse; use \AccelaSearch\ProductMapper\Stock\Warehouse\Physical as PhysicalWarehouse; use \AccelaSearch\ProductMapper\Stock\Quantity\Limited as LimitedQuantity; use \AccelaSearch\ProductMapper\Stock\Quantity\Unlimited as UnlimitedQuantity; $generic_warehouse = new VirtualWarehouse("warehouse-001"); $brick_warehouse = new PhysicalWarehouse("warehouse-002", 45.0, 13.5); $five_in_stock = new LimitedQuantity(5); $unlimited = new UnlimitedQuantity(); // $item = ... $item->getAvailability()->add(new Stock($generic_warehouse, $five_in_stock)) ->add(new Stock($brick_warehouse, $unlimited));
仓库和数量的任何组合都是允许的,每个组合都由Stock\Stock
类进行中介。
添加定价
价格信息始终与客户组相关联,以便为不同用户组提供不同的价格系统。此外,价格可能因购买数量而异(即多级定价),支持多种货币,并允许指定与列表价格不同的销售价格。在使用之前,必须创建(或从收集器读取)客户组,而层、货币以及销售和列表价格是Price\Price
类的标准属性。通过ProductFactory
创建产品时,将自动实例化一个空的Price\Pricing
,应通过其访问器方法访问。
use \AccelaSearch\ProductMapper\Price\CustomerGroup; use \AccelaSearch\ProductMapper\Price\Price; $group_1 = new CustomerGroup("standard-group"); $group_2 = new CustomerGroup("webpos-group"); // $item = ... // Item normally sold for 19.99 USD, now selling for 15.99, no tiers, only for standard-group $item->getPricing()->add(new Price(19.99, 15.99, "USD", 0, $group_1)); // Same item normally sold for 16.43 EUR, now selling at 13.14 EUR $item->getPricing()->add(new Price(19.99, 15.99, "USD", 0, $group_1)); // Item selling at 19.99 USD for quantities between 0 and 9, selling at 9.99 if 100 or more units are bought $item->getPricing()->add(new Price(19.99, 19.99, "USD", 0, $group_1)) ->add(new Price(9.99, 9.99, "USD", 100, $group_1)); // Same item, but different prices for different customer group, second group gets a discount $item->getPricing()->add(new Price(19.99, 19.99, "USD", 0, $group_1)) ->add(new Price(25.99, 21.50, "USD", 0, $group_1));
相同的货币、最小数量和组组合不能插入多次,否则会发生未定义的行为。
添加自定义属性
除了明确处理的属性(URL、sku、价格、可用性等)之外,还可以插入自定义属性。每个自定义属性都有一个名称和一系列值,即每个属性都被视为多值。单值必须以多值属性的形式插入,并且只有一个值。应在将属性分配给产品之前创建属性。
use \AccelaSearch\ProductMapper\Attribute; $name = new Attribute("name"); $name->addValue("T-Shirt"); $tags = new Attribute("tag"); $tags->addValue("fashion")->addValue("summer")->addValue("light"); // $item = ... $item->addAttribute($name) ->addAttribute($tags);
一旦为产品定义了属性,就可以通过其名称(和可能修改其值列表)检索它。
$item->getAttribute("tag")->removeValue("light")->addValue("men");
属性名称应该是英文单词,全部小写,单数形式(当适用时),并且不得包含空格。我们建议使用连字符或下划线代替空格。
对于单值属性,有一个简写形式的工厂方法。
$item->addAttribute(Attribute::fromNameAndValue("name", "T-Shirt"));
对于属于可配置产品的产品,影响配置的属性必须通过将isConfigurable
设置为true来标记为可配置
$color = new Attribute("color"); $color->addValue("red"); $color->setIsConfigurable(true); // $configurable_item = ... // $actual_item = ... $actual_item->addAttribute($color);
这也有简写形式。
$actual_item->addAttribute(Attribute::fromNameAndValue("color", "red")->setIsConfigurable(true));
可配置产品
可配置产品是元产品,逻辑上将一组实际(通常是Simple
)产品组合在一起,这些产品之间在细节上有所不同,例如颜色或尺寸。在处理可配置产品时,必须创建并分配一组子产品给父可配置产品。子产品的某些属性应标记为可配置,以便告诉AccelaSearch哪些属性构成配置,哪些属性不构成(如前所述)。
use \AccelaSearch\ProductMapper\ProductFactory; use \AccelaSearch\ProductMapper\Attribute; $factory = new ProductFactory(); $shirt = $factory->createConfigurable("http://www.myshop.com/shirt", "ID:42"); $shirt->setSku("CONF-001"); $blue_shirt = $factory->createSimple("http://www.myshop.com/shirt/blue", "ID:44"); $blue_shirt->setSku("CONF-001-b"); $blue_shirt->addAttribute(Attribute::fromNameAndValue("color", "blue")->setIsConfigurable(true)); $blue_shirt->addAttribute(Attribute::fromNameAndValue("name", "blue shirt")); $red_shirt = $factory->createSimple("http://www.myshop.com/shirt/red", "ID:45"); $red_shirt->setSku("CONF-001-r"); $red_shirt->addAttribute(Attribute::fromNameAndValue("color", "red")->setIsConfigurable(true)); $red_shirt->addAttribute(Attribute::fromNameAndValue("name", "red shirt")); $shirt->addVariant($red_shirt)->addVariant($blue_shirt);
尽管示例中没有显示,但建议为父产品和子产品设置定价、可用性和分类信息,因为前者不会从后者继承它们,反之亦然。
捆绑和分组产品
捆绑和分组是将一组实际(通常是Simple
)产品组合在一起进行销售的产品。在处理捆绑或分组产品时,必须创建并分配一组子产品给父产品。产品可以作为同一父产品的子产品出现多次,例如,当一组两个相同的产品一起销售时。
use \AccelaSearch\ProductMapper\ProductFactory; use \AccelaSearch\ProductMapper\Attribute; $factory = new ProductFactory(); $group = $factory->createGrouped("http://www.myshop.com/group", "ID:48"); $group->setSku("GRP-009"); $bottle = $factory->createSimple("http://www.myshop.com/bottle", "ID:94"); $bottle->setSku("BTL-001"); $paper = $factory->createSimple("http://www.myshop.com/paper", "ID:105"); $bottle->setSku("PPR-001"); // Two bottles and one piece of paper $group->addProduct($bottle)->addProduct($bottle)->addProduct($paper);
尽管示例中没有显示,但建议为父产品和子产品设置定价、可用性和分类信息,因为前者不会从后者继承它们,反之亦然。