如何覆盖 sylius shop api 插件上的用户注册

问题描述

我正在使用 Shop API plugin

设置 Sylius 1.8.6

要做的是在用户注册添加一些字段。通过添加到类 namespace App\Entity\User\ShopUser

,我设法将它们添加到 Sylius ShopUser 实体
namespace App\Entity\User;

use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Core\Model\ShopUser as BaseShopUser;

/**
 * @ORM\Entity
 * @ORM\Table(name="sylius_shop_user")
 */
class ShopUser extends BaseShopUser
{
    /**
     * @var string
     * @ORM\Column(type="string",nullable=false)
     */
    private string $permit;

    public function getPermit(): string
    {
        return $this->permit;
    }

    public function setPermit(string $permit): void
    {
        $this->permit = $permit;
    }
}

并通过创建一个 ShopUserFactory

<?PHP


namespace App\Fixtures;

use App\Entity\User\ShopUser;
use Sylius\Bundle\CoreBundle\Fixture\Factory\ShopUserExampleFactory;
use Sylius\Component\Core\Model\ShopUserInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ShopUserFactory extends ShopUserExampleFactory
{
    public function create(array $options = []): ShopUserInterface
    {
        /** @var ShopUser $user */
        $user = parent::create($options);

        if (isset($options['permit'])) {
            $user->setPermit($options['permit']);
        }

        return $user;
    }

    protected function configureOptions(OptionsResolver $resolver): void
    {
        parent::configureOptions($resolver);

        $resolver
            ->setDefault('permit','default_permit')
            ->setAllowedTypes('permit',['string'])
        ;
    }

}

一个 ShopUserFixture

<?PHP


namespace App\Fixtures;


use Sylius\Bundle\CoreBundle\Fixture\ShopUserFixture as ShopUserFixtureBase;
use Symfony\Component\Config\DeFinition\Builder\ArrayNodeDeFinition;

final class ShopUserFixture extends ShopUserFixtureBase
{
    protected function configureResourceNode(ArrayNodeDeFinition $resourceNode): void
    {
        parent::configureResourceNode($resourceNode);

        $resourceNode
            ->children()
            ->scalarNode('permit')->end();
    }
}

并将这两个添加到我的 services.yaml

  sylius.fixture.example_factory.shop_user:
    class: App\Fixtures\ShopUserFactory
    arguments:
      - "@sylius.factory.shop_user"
    public: true

  sylius.fixture.shop_user:
    class: App\Fixtures\ShopUserFixture
    arguments:
      - "@sylius.manager.shop_user"
      - "@sylius.fixture.example_factory.shop_user"
    tags:
      - { name: sylius_fixtures.fixture }

现在我想要做的是确保在 /register调用 api 时添加了这个我一直在关注 documentation 并创建了一个自定义Request,{{1} } 和 Handler

Command

我也将这些添加到我的 #Request <?PHP namespace App\Controller\ShopAPI\Requests; use App\Controller\ShopAPI\Commands\UserRegistrationCommand; use Sylius\ShopApiPlugin\Command\CommandInterface; use Sylius\ShopApiPlugin\Command\Customer\RegisterCustomer; use Sylius\ShopApiPlugin\Request\Customer\RegisterCustomerRequest; use Symfony\Component\HttpFoundation\RequestStack; final class UserRegistration extends RegisterCustomerRequest { private $address; private $city; private $postcode; private $permit; public function __construct(RequestStack $requestStack,string $channelCode) { $request = $requestStack->getCurrentRequest(); parent::__construct($request,$channelCode); $this->address = $request->request->get('address'); $this->postcode = $request->request->get('postcode'); $this->city = $request->request->get('city'); $this->permit = $request->request->get('permit'); var_dump($request->request->get('permit'));die; } public function getCommand(): CommandInterface { return new UserRegistrationCommand( $this->email,$this->plainPassword,$this->firstName,$this->lastName,$this->channelCode,$this->subscribedToNewsletter,$this->phoneNumber,$this->address,$this->postcode,$this->city,$this->permit ); } } #Command <?PHP namespace App\Controller\ShopAPI\Commands; use Sylius\ShopApiPlugin\Command\Customer\RegisterCustomer; class UserRegistrationCommand extends RegisterCustomer { protected string $address; protected string $city; protected string $postcode; protected string $permit; public function __construct( string $email,string $plainPassword,string $firstName,string $lastName,string $channelCode,?bool $subscribedToNewsletter,?string $phoneNumber,string $address,string $city,string $postcode,string $permit ) { parent::__construct( $email,$plainPassword,$firstName,$lastName,$channelCode,$subscribedToNewsletter,$phoneNumber ); $this->address = $address; $this->city = $city; $this->postcode = $postcode; $this->permit = $permit; } public function address(): string { return $this->address; } public function city(): string { return $this->city; } public function postcode(): string { return $this->postcode; } public function permit(): string { return $this->permit; } } #Handler <?PHP declare(strict_types=1); namespace App\Controller\ShopAPI\Handlers; use App\Controller\ShopAPI\Commands\UserRegistrationCommand; use App\Entity\User\ShopUser; use Sylius\Component\Channel\Repository\ChannelRepositoryInterface; use Sylius\Component\Core\Model\AddressInterface; use Sylius\Component\Core\Model\ShopUserInterface; use Sylius\Component\Core\Repository\AddressRepositoryInterface; use Sylius\Component\Resource\Factory\FactoryInterface; use Sylius\Component\User\Repository\UserRepositoryInterface; use Sylius\ShopApiPlugin\Event\CustomerRegistered; use Sylius\ShopApiPlugin\Provider\CustomerProviderInterface; use Symfony\Component\Eventdispatcher\EventdispatcherInterface; use Webmozart\Assert\Assert; final class UserRegistrationHandler { /** @var UserRepositoryInterface */ private $userRepository; /** @var ChannelRepositoryInterface */ private $channelRepository; /** @var FactoryInterface */ private $userFactory; /** @var EventdispatcherInterface */ private $eventdispatcher; /** @var CustomerProviderInterface */ private $customerProvider; /** @var FactoryInterface */ private FactoryInterface $addressFactory; /** @var AddressRepositoryInterface */ private AddressRepositoryInterface $addressRepository; public function __construct( UserRepositoryInterface $userRepository,AddressRepositoryInterface $addressRepository,ChannelRepositoryInterface $channelRepository,FactoryInterface $userFactory,FactoryInterface $addressFactory,EventdispatcherInterface $eventdispatcher,CustomerProviderInterface $customerProvider ) { $this->userRepository = $userRepository; $this->channelRepository = $channelRepository; $this->userFactory = $userFactory; $this->eventdispatcher = $eventdispatcher; $this->customerProvider = $customerProvider; $this->addressFactory = $addressFactory; $this->addressRepository = $addressRepository; } public function __invoke(UserRegistrationCommand $command): void { $this->assertEmailIsNottaken($command->email()); $this->assertChannelExists($command->channelCode()); $customer = $this->customerProvider->provide($command->email()); $customer->setFirstName($command->firstName()); $customer->setLastName($command->lastName()); $customer->setEmail($command->email()); $customer->setSubscribedToNewsletter($command->subscribedToNewsletter()); $customer->setPhoneNumber($command->phoneNumber()); /** @var ShopUser $user */ $user = $this->userFactory->createNew(); $user->setPlainPassword($command->plainPassword()); $user->setUsername($command->email()); $user->setPermit($command->permit()); $user->setCustomer($customer); $this->userRepository->add($user); /** @var AddressInterface $address */ $address = $this->addressFactory->createNew(); $address->setPostcode($command->postcode()); $address->setPhoneNumber($command->phoneNumber()); $address->setCity($command->city()); $address->setCustomer($user); $this->addressRepository->add($address); $customer->setDefaultAddress($address); $this->eventdispatcher->dispatch('sylius.customer.post_api_registered',new CustomerRegistered( $command->email(),$command->firstName(),$command->lastName(),$command->channelCode(),$command->subscribedToNewsletter(),$command->phoneNumber() )); } private function assertEmailIsNottaken(string $email): void { Assert::null($this->userRepository->findOneByEmail($email),'User with given email already exists.'); } private function assertChannelExists(string $channelCode): void { Assert::notNull($this->channelRepository->findOneByCode($channelCode),'Channel does not exist.'); } }

services.yaml

正如文档所说的覆盖处理程序

扩展处理程序的主要方式是装饰它。这使得在处理程序之前和之后添加功能变得容易。但是,如果要更改处理程序中的逻辑,则需要覆盖它。这可以通过使用相同的服务 ID 注册新的处理程序来完成。

我是这么想的:

  App\Controller\ShopAPI\Commands\UserRegistrationCommand:
    arguments:
      $email: "%email%"

  App\Controller\ShopAPI\Handlers\UserRegistrationHandler:
    public: true
    arguments:
      $userRepository: '@sylius.repository.shop_user'
      $userFactory: '@sylius.factory.shop_user'
      $customerProvider: '@sylius.shop_api_plugin.provider.customer_provider'
    
  Sylius\ShopApiPlugin\Command\Customer\RegisterCustomer:
    class: App\Controller\ShopAPI\Handlers\UserRegistrationHandler

  App\Controller\ShopAPI\Requests\UserRegistration:
    arguments:
      $channelCode: "%channelCode%"

将使请求通过我的类而不是认值,但是我不断收到错误

Sylius\ShopApiPlugin\Command\Customer\RegisterCustomer:
    class: App\Controller\ShopAPI\Handlers\UserRegistrationHandler

并且在使用 xdebug 或什至在这三个类中的任何一个添加一些 { "code": 500,"message": "An exception occurred while executing 'INSERT INTO sylius_shop_user (username,username_canonical,enabled,salt,password,encoder_name,last_login,password_reset_token,password_requested_at,email_verification_token,verified_at,locked,expires_at,credentials_expire_at,roles,email,email_canonical,created_at,updated_at,permit,customer_id) VALUES (?,?,?)' with params [\"test3@example.com\",\"test3@example.com\",\"ketyu603mrk0ksg0s0ssc0wkcw44k8g\",\"$argon2i$v=19$m=65536,t=4,p=1$Z09IeWlJR05nSW40cVYuYg$JIUtgpsZRVnKJoJJZvfN+kX5XRF+U69t8SQzRdZTVOs\",\"argon2i\",null,\"a:1:{i:0;s:9:\\\"ROLE_USER\\\";}\",\"2020-12-22 08:41:08\",102]:\n\nsqlSTATE[23000]: Integrity constraint violation: 1048 Column 'permit' cannot be null" } 时,代码永远不会通过它们。

我没有正确注册新服务吗?我找不到任何关于此的信息。

感谢任何为我指明正确方向的人。

解决方法

好的,我开始工作了。设置所有服务后,我仍然遇到错误。这是因为我的

use Sylius\ShopApiPlugin\Command\Customer\RegisterCustomer;

class UserRegistrationCommand extends RegisterCustomer

并且 ChannelBasedCommandProvider 会调用

 $this->requestClass::fromHttpRequestAndChannel($httpRequest,$channel);

而且我没有在我的子类中覆盖这个函数:

    public static function fromHttpRequestAndChannel(Request $request,ChannelInterface $channel): ChannelBasedRequestInterface
   {
       return new self($request,$channel->getCode());
   }

因此它将返回父类,并且该类不再有处理程序。