Skip to content
Advertisement

How to overide the user registration on the sylius shop api plugin

I am setting up a Sylius 1.8.6 with the Shop API plugin

What needs to be done is adding some fields on the user registration. I have managed to add them to the Sylius ShopUser entity by adding to the class namespace AppEntityUserShopUser

namespace AppEntityUser;

use DoctrineORMMapping as ORM;
use SyliusComponentCoreModelShopUser as BaseShopUser;

/**
 * @ORMEntity
 * @ORMTable(name="sylius_shop_user")
 */
class ShopUser extends BaseShopUser
{
    /**
     * @var string
     * @ORMColumn(type="string", nullable=false)
     */
    private string $permit;

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

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

And to the fixtures by creating a ShopUserFactory

<?php


namespace AppFixtures;

use AppEntityUserShopUser;
use SyliusBundleCoreBundleFixtureFactoryShopUserExampleFactory;
use SyliusComponentCoreModelShopUserInterface;
use SymfonyComponentOptionsResolverOptionsResolver;

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'])
        ;
    }

}

and a ShopUserFixture

<?php


namespace AppFixtures;


use SyliusBundleCoreBundleFixtureShopUserFixture as ShopUserFixtureBase;
use SymfonyComponentConfigDefinitionBuilderArrayNodeDefinition;

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

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

And adding the two to my services.yaml

  sylius.fixture.example_factory.shop_user:
    class: AppFixturesShopUserFactory
    arguments:
      - "@sylius.factory.shop_user"
    public: true

  sylius.fixture.shop_user:
    class: AppFixturesShopUserFixture
    arguments:
      - "@sylius.manager.shop_user"
      - "@sylius.fixture.example_factory.shop_user"
    tags:
      - { name: sylius_fixtures.fixture }

Now what I want to do is make sure this is added when the api is called on /register I have been following the documentation and created a custom Request, Handler and Command

#Request 
<?php


namespace AppControllerShopAPIRequests;


use AppControllerShopAPICommandsUserRegistrationCommand;
use SyliusShopApiPluginCommandCommandInterface;
use SyliusShopApiPluginCommandCustomerRegisterCustomer;
use SyliusShopApiPluginRequestCustomerRegisterCustomerRequest;
use SymfonyComponentHttpFoundationRequestStack;

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 AppControllerShopAPICommands;


use SyliusShopApiPluginCommandCustomerRegisterCustomer;

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 AppControllerShopAPIHandlers;

use AppControllerShopAPICommandsUserRegistrationCommand;
use AppEntityUserShopUser;
use SyliusComponentChannelRepositoryChannelRepositoryInterface;
use SyliusComponentCoreModelAddressInterface;
use SyliusComponentCoreModelShopUserInterface;
use SyliusComponentCoreRepositoryAddressRepositoryInterface;
use SyliusComponentResourceFactoryFactoryInterface;
use SyliusComponentUserRepositoryUserRepositoryInterface;
use SyliusShopApiPluginEventCustomerRegistered;
use SyliusShopApiPluginProviderCustomerProviderInterface;
use SymfonyComponentEventDispatcherEventDispatcherInterface;
use WebmozartAssertAssert;

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.');
    }
}

And I have also added these to my services.yaml

  AppControllerShopAPICommandsUserRegistrationCommand:
    arguments:
      $email: "%email%"

  AppControllerShopAPIHandlersUserRegistrationHandler:
    public: true
    arguments:
      $userRepository: '@sylius.repository.shop_user'
      $userFactory: '@sylius.factory.shop_user'
      $customerProvider: '@sylius.shop_api_plugin.provider.customer_provider'
    
  SyliusShopApiPluginCommandCustomerRegisterCustomer:
    class: AppControllerShopAPIHandlersUserRegistrationHandler

  AppControllerShopAPIRequestsUserRegistration:
    arguments:
      $channelCode: "%channelCode%"

As the documentation says about overriding handlers

The main way to extend a handler is to decorate it. This makes adding functionality before and after the handler easy. However, if you want to change the logic in the handler, you need to overwrite it. This can be done by registering the new handler with the same service id.

I thought this bit :

SyliusShopApiPluginCommandCustomerRegisterCustomer:
    class: AppControllerShopAPIHandlersUserRegistrationHandler

would make the request go through my class instead of the default however I keep getting the error :

{
    "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]:nnSQLSTATE[23000]: Integrity constraint violation: 1048 Column 'permit' cannot be null"
}

And while using xdebug or even adding some dd()‘s inside any of those three classes the code never goes through them.

Am I not registering the new service correctly ? I can’t find anything regarding this.

Thanks for any one pointing me in the right direction.

Advertisement

Answer

Ok I got it working. After setting up all the services I still had the error. This was because of my

use SyliusShopApiPluginCommandCustomerRegisterCustomer;

class UserRegistrationCommand extends RegisterCustomer

and the ChannelBasedCommandProvider would call

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

And I did not override this function in my child class :

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

So it would return the parent class instead and there is no more handler for that class.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement