Benjamin Cremer
Core Developer shopware AG
Follow me @benjamincremer
BaseProduct < ListProduct < Product
BaseProduct | Product Identification |
ListProduct | Basic Product Data (Category Listing, Related Products) |
Product | Full Product data, contains ListProducts (Product Detail Page) |
$criteria = new \Shopware\Bundle\SearchBundle\Criteria();
$criteria->addCondition(
new HasFreeShippingCondition()
);
$criteria->addCondition(
new SearchTermCondition('tee')
);
$criteria->addCondition(
new PriceRangeCondition(5, 50)
);
$criteria->addSorting(
new PriceSorting(SortingInterface::SORT_ASC)
);
$criteria->limit(5);
$criteria->offset(0);
namespace Shopware\Bundle\SearchBundle\Condition;
class PriceRangeCondition implements ConditionInterface
{
/**
* @param float $minPrice
* @param float $maxPrice
*/
public function __construct($minPrice, $maxPrice)
{
Assertion::float($minPrice); // see github.com/beberlei/assert
Assertion::float($maxPrice);
$this->minPrice = $minPrice;
$this->maxPrice = $maxPrice;
}
public function getMinPrice() {};
public function getMaxPrice() {};
}
/** @var ContextServiceInterface $contextService */
$contextService = $container->get('shopware_storefront.context_service_core');
$context = $contextService->getProductContext();
$context->getShop()->getId(); // 1
$context->getFallbackCustomerGroup()->getKey(); // EK
namespace Shopware\Bundle\SearchBundle;
interface ProductNumberSearchInterface
{
/**
* @return ProductNumberSearchResult
*/
public function search(Criteria $criteria, ContextInterface $context);
}
$criteria = Criteria();
$criteria->addCondition(new SearchTermCondition('tee'));
$criteria->limit(5);
[..]
$context = [..]
/** @var ProductNumberSearchInterface $search */
$search = $this->get('shopware_searchdbal.product_number_search');
/** @var ProductNumberSearchResult $result */
$result = $search->search($criteria, $context);
echo $result->getTotalCount();
/** @var BaseProduct $product */
foreach ($result->getProducts() as $product) {
echo $product->getNumber();
}
interface ListProductServiceInterface
{
/**
* @return Struct\ListProduct
*/
public function get($number, ContextInterface $context);
/**
* @return Struct\ListProduct[] Indexed by the products order number
*/
public function getList(array $numbers, ContextInterface $context);
}
interface ProductServiceInterface
{
/**
* @return Struct\Product
*/
public function get($number, ContextInterface $context);
/**
* @return Struct\Product[] Indexed by the products order number
*/
public function getList($numbers, ContextInterface $context);
}
/** @var ListProductService $productListService */
$productListService = $this->get('shopware_storefront.list_product_service');
/** @var ListProduct $product */
$product = $productListService->get('sw-555', $context);
/** @var ListProduct[] $products */
$products = $productListService->getList(['sw-555', 'sw-111'], $context);
/** @var ProductNumberSearchResult $searchResult */
$searchResult = $this->productNumberSearch->search($condition, $context);
/** @var ListProductService $productListService */
$productListService = $this->get('shopware_storefront.list_product_service');
$listProducts = [];
foreach ($result->getProducts() as $product) {
$listProducts = $productListService->get($product->getNumber(), $context);
}
// Better:
$listProducts = $productListService->getList(
array_keys($result->getProducts()),
$context
);
// Produces one single IN query (default DBAL implementation)
namespace Shopware\Bundle\SearchBundle;
interface ProductSearchInterface
{
/**
* @return ProductSearchResult
*/
public function search(Criteria $criteria, ContextInterface $context);
}
class ProductSearch implements ProductSearchInterface
{
public function __construct(
ProductNumberSearchInterface $productSearch // Doctrine DBAL
ListProductServiceInterface $productService, // Doctrine DBAL
);
/**
* @return ProductSearchResult
*/
public function search(Criteria $criteria, ContextInterface $context);
}
class ProductSearch implements ProductSearchInterface
{
public function __construct(
ProductNumberSearchInterface $productSearch // Elasticsearch
ListProductServiceInterface $productService, // Doctrine DBAL
);
/**
* @return ProductSearchResult
*/
public function search(Criteria $criteria, ContextInterface $context);
}
class ProductSearch implements ProductSearchInterface
{
public function __construct(
ProductNumberSearchInterface $productSearch // Elasticsearch
ListProductServiceInterface $productService, // Elasticsearch
);
/**
* @return ProductSearchResult
*/
public function search(Criteria $criteria, ContextInterface $context);
}
$condition = [..];
$context = [..];
/** @var ProductSearchInterface $search */
$search = $this->get('shopware_search.product_search');
$result = $search->search($criteria, $context);
echo "Totalcount: ". $result->getTotalCount(). "\n";
/** @var ListProduct $product */
foreach ($result->getProducts() as $product) {
echo $product->getName();
};
interface ConditionHandlerInterface
{
/**
* @param ConditionInterface $condition
* @return bool
*/
public function supportsCondition(ConditionInterface $condition);
/**
* @param ConditionInterface $condition
* @param QueryBuilder $query
* @param ContextInterface $context
*/
public function generateCondition(
ConditionInterface $condition,
QueryBuilder $query,
ContextInterface $context
);
}
class ManufacturerConditionHandler implements ConditionHandlerInterface
{
public function supportsCondition(ConditionInterface $condition)
{
return ($condition instanceof ManufacturerCondition);
}
public function generateCondition(
ConditionInterface $condition,
QueryBuilder $query,
ContextInterface $context
) {
/* @var $condition ManufacturerCondition */
$query->innerJoin(
'product',
's_articles_manufacturer',
'manufacturer',
'manufacturer.id = product.manufacturerID AND product.manufacturerID IN (:manufacturer)'
);
$query->setParameter(
':manufacturer',
$condition->getManufacturerIds(),
Connection::PARAM_INT_ARRAY
);
}
}
class ProductNameSortingHandler implements SortingHandlerInterface
{
public function supportsSorting(SortingInterface $sorting)
{
return ($sorting instanceof ProductNameSorting);
}
public function generateSorting(
SortingInterface $sorting,
QueryBuilder $query,
ContextInterface $context
) {
$query->addOrderBy('product.name', $sorting->getDirection())
->addOrderBy('product.id', $sorting->getDirection());
}
}
“Elasticsearch is a search server based on Lucene. It provides a distributed, multitenant-capable full-text search engine with a RESTful web interface and schema-free JSON documents.”
https://en.wikipedia.org/wiki/Elasticsearchtideways.io/profiler/blog/high-performance-shopware-51-with-elasticsearch
class SalesConditionHandler implements HandlerInterface
{
public function supports(CriteriaPartInterface $criteriaPart)
{
return ($criteriaPart instanceof SalesCondition);
}
public function handle(
CriteriaPartInterface $criteriaPart,
Criteria $criteria,
Search $search,
ContextInterface $context
) {
$filter = new RangeFilter(
'sales',
['gt' => $criteriaPart->getMinSales()]
);
$search->addFilter($filter);
}
}
100.000 Producs / 100 Categories
MySQL
Elasticsearch
tideways.io/profiler/blog/high-performance-shopware-51-with-elasticsearch
Please give feedback