如何在Shopware的自定义非继承类中访问数据库层(无需container->get()或DI服务)
在Shopware自定义类中无需手动获取容器即可访问数据库的解决方案
首先,Shopware的最佳实践绝对不推荐手动调用container->get()或者直接获取容器实例——这会破坏依赖注入的设计原则,让代码难以测试和维护。你的DiscountCollector已经通过构造函数注入了PercentagePriceCalculator,我们只需要沿用这个模式,注入你需要的数据库访问类即可。
方法1:注入Shopware专用Repository(推荐)
Shopware为每个实体都提供了专用的Repository类,这些类封装了Doctrine的数据库操作,并且适配Shopware的上下文系统(比如多语言、多店铺)。
步骤1:修改类的构造函数,注入目标Repository
比如如果你需要操作产品数据,就注入ProductRepository:
<?php declare(strict_types=1); namespace Author\\PriceDiscountPlugin\\Handlers; use Shopware\\Core\\Checkout\\Cart\\Cart; use Shopware\\Core\\Checkout\\Cart\\CartBehavior; use Shopware\\Core\\Checkout\\Cart\\CartProcessorInterface; use Shopware\\Core\\Checkout\\Cart\\LineItem\\CartDataCollection; use Shopware\\Core\\Checkout\\Cart\\LineItem\\LineItem; use Shopware\\Core\\Checkout\\Cart\\LineItem\\LineItemCollection; use Shopware\\Core\\Checkout\\Cart\\Price\\PercentagePriceCalculator; use Shopware\\Core\\Checkout\\Cart\\Price\\Struct\\PercentagePriceDefinition; use Shopware\\Core\\Checkout\\Cart\\Rule\\LineItemRule; use Shopware\\Core\\System\\SalesChannel\\SalesChannelContext; // 引入需要的Repository和查询条件类 use Shopware\\Core\\Content\\Product\\ProductRepository; use Shopware\\Core\\Framework\\DataAbstractionLayer\\Search\\Criteria; class DiscountCollector implements CartProcessorInterface { /** * @var PercentagePriceCalculator */ private $calculator; /** * @var ProductRepository */ private $productRepository; // 修改构造函数,注入ProductRepository public function __construct(PercentagePriceCalculator $calculator, ProductRepository $productRepository) { $this->calculator = $calculator; $this->productRepository = $productRepository; } public function process(CartDataCollection $data, Cart $original, Cart $toCalculate, SalesChannelContext $context, CartBehavior $behavior): void { // 示例:查询某个产品 $productId = '...'; // 替换成你的目标产品ID $criteria = new Criteria([$productId]); // 必须传入context的内部Context对象,适配多语言/多店铺数据 $product = $this->productRepository->search($criteria, $context->getContext())->first(); // 接下来就可以使用$product的属性,比如$product->getPrice()、$product->getName() // 你的数据库操作逻辑... } }
步骤2:在插件中注册服务
Shopware的DI容器只会自动注入已注册的服务,所以你需要在插件的配置文件中声明这个类的服务:
如果使用XML配置(src/Resources/config/services.xml):
<?xml version="1.0" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <service id="Author\PriceDiscountPlugin\Handlers\DiscountCollector"> <argument type="service" id="Shopware\Core\Checkout\Cart\Price\PercentagePriceCalculator"/> <argument type="service" id="Shopware\Core\Content\Product\ProductRepository"/> </service> </services> </container>
如果使用PHP配置(src/Resources/config/services.php):
use Author\PriceDiscountPlugin\Handlers\DiscountCollector; use Shopware\Core\Checkout\Cart\Price\PercentagePriceCalculator; use Shopware\Core\Content\Product\ProductRepository; use Symfony\Component\DependencyInjection\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); $services->set(DiscountCollector::class) ->arg('$calculator', service(PercentagePriceCalculator::class)) ->arg('$productRepository', service(ProductRepository::class)); };
方法2:注入通用EntityManagerInterface
如果你需要执行更灵活的Doctrine原生操作(比如自定义DQL),可以注入EntityManagerInterface:
步骤1:修改构造函数注入EntityManager
// 引入Doctrine的EntityManager接口 use Doctrine\ORM\EntityManagerInterface; use Shopware\Core\Content\Product\ProductEntity; class DiscountCollector implements CartProcessorInterface { /** * @var PercentagePriceCalculator */ private $calculator; /** * @var EntityManagerInterface */ private $entityManager; public function __construct(PercentagePriceCalculator $calculator, EntityManagerInterface $entityManager) { $this->calculator = $calculator; $this->entityManager = $entityManager; } public function process(CartDataCollection $data, Cart $original, Cart $toCalculate, SalesChannelContext $context, CartBehavior $behavior): void { // 示例:通过Doctrine Repository查询 $productRepository = $this->entityManager->getRepository(ProductEntity::class); $product = $productRepository->findOneBy(['id' => $productId], null, $context->getContext()); // 或者执行自定义DQL查询 $query = $this->entityManager->createQuery( 'SELECT p FROM Shopware\Core\Content\Product\ProductEntity p WHERE p.id = :productId' )->setParameter('productId', $productId); $product = $query->getOneOrNullResult(); } }
步骤2:注册服务时注入EntityManager
XML配置中替换第二个参数:
<argument type="service" id="Doctrine\ORM\EntityManagerInterface"/>
PHP配置中:
->arg('$entityManager', service(EntityManagerInterface::class))
关键注意事项
- 永远不要手动获取容器:依赖注入是Shopware和Symfony的核心设计,手动调用
container->get()会让代码耦合度极高,难以测试和扩展。 - 必须传递Context对象:Shopware的
SalesChannelContext包含当前的店铺、语言、用户等上下文信息,传递给Repository的search方法才能确保获取到正确的数据(比如多语言的产品名称)。 - 优先使用Shopware专用Repository:专用Repository已经适配了Shopware的数据结构和业务逻辑,比直接使用Doctrine的EntityManager更符合Shopware的开发规范。
内容的提问来源于stack exchange,提问作者Mahmoud Abdelsattar
相关产品推荐
相关产品推荐

