ikucheriavenko
7/21/2019 - 8:50 PM

Basic Symfony components sample

Basic Symfony components sample

<?php

namespace App\EventListener;

use App\DTO\Publisher\CreatedTransactionDTO;
use App\Event\TransactionEvent;
use App\Events;
use DataMapper\MapperInterface;
use MessageBusBundle\Publisher\MessagePublisherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Class TransactionSubscriber
 */
class TransactionSubscriber implements EventSubscriberInterface
{
    /**
     * @var MessagePublisherInterface
     */
    private $publisher;

    /**
     * @var MapperInterface
     */
    private $mapper;

    /**
     * TransactionSubscriber constructor.
     *
     * @param MessagePublisherInterface $eventsPublisher
     * @param MapperInterface           $mapper
     */
    public function __construct(MessagePublisherInterface $eventsPublisher, MapperInterface $mapper)
    {
        $this->publisher = $eventsPublisher;
        $this->mapper = $mapper;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents(): array
    {
        return [
            Events::PAYMENT_TRANSACTION_CREATED => 'onTransactionCreated',
        ];
    }

    /**
     * @param TransactionEvent $event
     *
     * @return void
     */
    public function onTransactionCreated(TransactionEvent $event): void
    {
        $dto = $this->mapper->convert($event->getTransaction(), CreatedTransactionDTO::class);
        $data = $this->mapper->extract($dto);
        $this->publisher->publish('transaction.created', $data);
    }
}

----------------
<?php

namespace App\Processor\Payment;

use App\Entity\AbstractPayment;
use App\Entity\Payment;
use App\Manager\AbstractPaymentsManager;
use App\Specification\AbstractPayment\PaymentCallbackSpecification;
use App\Exception as PaymentException;
use App\Specification\RecurringPayment\RecurringPaymentByObjectIdSpecification;

/**
 * Class PaymentFinder
 */
class PaymentFinder
{
    /**
     * @var AbstractPaymentsManager
     */
    private $paymentsManager;

    /**
     * PaymentFinder constructor.
     *
     * @param AbstractPaymentsManager $paymentsManager
     */
    public function __construct(AbstractPaymentsManager $paymentsManager)
    {
        $this->paymentsManager = $paymentsManager;
    }

    /**
     * @throws PaymentException\PaymentNotFoundException
     *
     * @param string $paymentId
     *
     * @return AbstractPayment
     */
    public function lookupPayment(string $paymentId): AbstractPayment
    {
        $spec = new PaymentCallbackSpecification($paymentId);

        /** @var Payment $payment */
        if (!$payment = $this->paymentsManager->findOne($spec)) {
            throw new PaymentException\PaymentNotFoundException();
        }

        return $payment;
    }

    /**
     * @throws PaymentException\PaymentNotFoundException
     *
     * @param string $objectId
     *
     * @return AbstractPayment
     */
    public function lookupPaymentByObjectId(string $objectId): AbstractPayment
    {
        $spec = new RecurringPaymentByObjectIdSpecification($objectId);

        /** @var Payment $payment */
        if (!$payment = $this->paymentsManager->findOne($spec)) {
            throw new PaymentException\PaymentNotFoundException();
        }

        return $payment;
    }
}
----------------
<?php

namespace App\Command;

use App\Entity\RecurringPayment;
use App\Manager\RecurringPaymentsManager;
use App\Provider\PaymentProviderLoader;
use App\Specification\RecurringPayment\RecurringPaymentByIdSpecification;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class CancelSubscriptionCommand
 */
class CancelSubscriptionCommand extends Command
{
    private const CANCEL_ACTION = 'cancel-subscription';

    /**
     * @var string
     */
    protected static $defaultName = 'app:subscription:cancel';

    /**
     * @var PaymentProviderLoader
     */
    private $paymentProviderLoader;

    /**
     * @var RecurringPaymentsManager
     */
    private $recurringPaymentsManager;

    /**
     * CancelSubscriptionCommand constructor.
     *
     * @param null|string              $name
     * @param PaymentProviderLoader    $paymentProviderLoader
     * @param RecurringPaymentsManager $recurringPaymentsManager
     */
    public function __construct(
        ?string $name = null,
        PaymentProviderLoader $paymentProviderLoader,
        RecurringPaymentsManager $recurringPaymentsManager
    ) {
        parent::__construct($name);
        $this->paymentProviderLoader = $paymentProviderLoader;
        $this->recurringPaymentsManager = $recurringPaymentsManager;
    }

    /**
     * @return void
     */
    protected function configure(): void
    {
        $this
            ->setName('app:subscription:cancel')
            ->setDescription('Cancel subscription on payment provider side.')
            ->addOption(
                'payment-id',
                'p-id',
                InputOption::VALUE_REQUIRED,
                'Recurring payment id or external id'
            );
    }

    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     *
     * @return void
     *
     * @throws \Throwable
     */
    protected function execute(InputInterface $input, OutputInterface $output): void
    {
        $paymentId = $input->getOption('payment-id');
        /** @var RecurringPayment $recurringPayment */
        $recurringPayment = $this->recurringPaymentsManager->findOne(new RecurringPaymentByIdSpecification($paymentId));
        if (null !== $recurringPayment) {
            $subscriptionCancelAction = $this->paymentProviderLoader->loadProviderAction(
                $recurringPayment->getProvider(),
                self::CANCEL_ACTION
            );

            $subscriptionCancelAction->handle(
                function () {
                },
                $recurringPayment->getExtId()
            );
            $output->writeln(
                "{$recurringPayment->getProvider()} subscription 
                for Recurring Payment with id: $paymentId was cancelled"
            );
        } else {
            $output->writeln("Can't find Recurring Payment with following id or external id: $paymentId");
        }
    }
}
-------------
<?php
declare(strict_types=1);

namespace App\Controller;

use App\Document\Player;
use App\DTO\Internal\PlayerDTO;
use App\DTO\Request\Player\UpdatePlayerDTO;
use App\DTO\Response\PlayerDTO as ResponsePlayerDTO;
use App\Manager\PlayerManager;
use RestBundle\Configuration\Entity;
use RestBundle\Configuration\MapperParamConverter;
use RestBundle\Controller\RestController;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @IsGranted("IS_AUTHENTICATED_FULLY")
 */
class PlayerController extends RestController
{
    /**
     * @var PlayerManager
     */
    private $playerManager;

    /**
     * PlayerController constructor.
     *
     * @param PlayerManager $playerManager
     */
    public function __construct(PlayerManager $playerManager)
    {
        $this->playerManager = $playerManager;
    }

    /**
     * @Route(
     *     path="/player/{id}",
     *     methods={"PUT"},
     *     name="update_player"
     * )
     *
     * @MapperParamConverter("updateDTO", class="App\DTO\Request\Player\UpdatePlayerDTO")
     *
     * @Entity("player", expr="repository.findOneBy({id: id})")
     *
     * @param Player          $player
     * @param UpdatePlayerDTO $updateDTO
     *
     * @return Response
     */
    public function updatePlayer(Player $player, UpdatePlayerDTO $updateDTO): Response
    {
        /** @var PlayerDTO $playerDTO */
        $playerDTO = $this->mapper->convert($updateDTO, PlayerDTO::class);
        $playerDTO->userId = $this->getUser();

        $player = $this->playerManager->update($player, $playerDTO);

        return $this->createResponse($player, ResponsePlayerDTO::class);
    }
}
------------
<?php

namespace XXX\ElasticaBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

/**
 * Class ElasticaExtension.
 */
class ElasticaExtension extends Extension
{
    /**
     * {@inheritdoc}
     *
     * @throws \LogicException
     * @throws \ReflectionException
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');
        $loader->load('admin.yml');

        $bundles = $container->getParameter('kernel.bundles');
        if (isset($bundles['ElasticaBundle'])) {
            $this->addCustomFormTypes($container);
        }

        $this->loadTypeMapping($config['types'], $container);
        $this->loadWebhooksRegistry($config['webhooks'], $container);
    }

    /**
     * @param array            $webhooks
     * @param ContainerBuilder $container
     */
    private function loadWebhooksRegistry(array $webhooks, ContainerBuilder $container): void
    {
        $registryDef = $container->getDefinition(WebhookRegistry::class);

        foreach ($webhooks as $webhook) {
            $registryDef->addMethodCall('addWebhook', [$webhook]);
        }
    }

    /**
     * @param array            $types
     * @param ContainerBuilder $container
     *
     * @throws \LogicException
     * @throws \ReflectionException
     */
    private function loadTypeMapping(array $types, ContainerBuilder $container): void
    {
        foreach ($types as $name => $type) {
            $typeConfig = [
                'name' => $type['type_name'] ?? $name,
                'transformer' => $type['transformer'],
                'mapping' => [], // An array containing anything that gets sent directly to ElasticSearch
                'config' => [],
            ];

            foreach (['dynamic_templates', 'properties', '_all', '_id', '_parent', '_routing', '_source'] as $field) {
                if (isset($type[$field])) {
                    $typeConfig['mapping'][$field] = $type[$field];
                }
            }

            $this->loadModelTransformer($typeConfig, $container);
        }
    }

    /**
     * @param array            $typeConfig
     * @param ContainerBuilder $container
     *
     * @throws \LogicException
     * @throws \ReflectionException
     */
    private function loadModelTransformer(array $typeConfig, ContainerBuilder $container): void
    {
        $transformerDef = $container->getDefinition($transformerClass = $typeConfig['transformer']);
        $reflClass = $container->getReflectionClass($transformerClass, false);

        if (!$reflClass || !$reflClass->implementsInterface(ModelToElasticaTransformerInterface::class)) {
            throw new \LogicException(
                sprintf(
                    '%s should implement the %s interface when used as model transformer.',
                    $transformerClass,
                    ModelToElasticaTransformerInterface::class
                )
            );
        }

        $transformerDef->addMethodCall('setModelMapping', [$typeConfig['mapping']['properties']]);

        $this->checkTransformerConfig($typeConfig['mapping']['properties'], $container);
    }

    /**
     * @param array            $typeConfig
     * @param ContainerBuilder $container
     *
     * @throws \LogicException
     * @throws \ReflectionException
     */
    private function checkTransformerConfig(array $typeConfig, ContainerBuilder $container): void
    {
        $transformers = (new ArraySearch($typeConfig))->search(['transformer']);
        foreach ($transformers as $transformerClass) {
            $reflClass = $container->getReflectionClass($transformerClass, false);

            if (!$reflClass || !$reflClass->implementsInterface(ElasticaTransformerInterface::class)) {
                throw new \LogicException(
                    sprintf(
                        '%s should implement the %s interface when used as transformer.',
                        $transformerClass,
                        ElasticaTransformerInterface::class
                    )
                );
            }
        }
    }

    /**
     * @param ContainerBuilder $container
     */
    private function addCustomFormTypes(ContainerBuilder $container): void
    {
        $resources = [];
        if ($container->hasParameter('twig.form.resources')) {
            $resources = $container->getParameter('twig.form.resources');
        }

        $resources[] = 'ElasticaBundle:Form:fields.html.twig';

        $container->setParameter('twig.form.resources', $resources);
    }
}