首页 > 解决方案 > 如何覆盖 sylius shop api 插件上的用户注册

问题描述

我正在使用Shop API 插件设置 Sylius 1.8.6

需要做的是在用户注册上添加一些字段。我设法通过添加到类将它们添加到 Sylius ShopUser 实体namespace App\Entity\User\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 }

现在我要做的是确保在调用 api 时添加它/register我一直在关注文档并创建了一个 custom RequestHandler并且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

  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%"

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

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

我想到了这一点:

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

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

{
    "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\", 0, \"ketyu603mrk0ksg0s0ssc0wkcw44k8g\", \"$argon2i$v=19$m=65536,t=4,p=1$Z09IeWlJR05nSW40cVYuYg$JIUtgpsZRVnKJoJJZvfN+kX5XRF+U69t8SQzRdZTVOs\", \"argon2i\", null, null, null, null, null, 0, null, null, \"a:1:{i:0;s:9:\\\"ROLE_USER\\\";}\", null, null, \"2020-12-22 08:41:08\", \"2020-12-22 08:41:08\", null, 102]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1048 Column 'permit' cannot be null"
}

在使用 xdebug 甚至dd()在这三个类中的任何一个中添加一些 ' 时,代码永远不会通过它们。

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

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

标签: phpsymfonysylius

解决方案


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

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());
   }

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


推荐阅读