Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to dbal type & make strategies configurable #11

Merged
merged 6 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ Rule is based on [regular expression](https://en.wikipedia.org/wiki/Regular_expr
#### Negated RegEx
Based on the RegEx strategy, but negates the result. Helpful if only a few pages should be restricted.

### Add custom strategy

The strategy configuration is meant to be extensible. You can create an own strategy by creating a new class that
implements the `\BitExpert\SyliusForceCustomerLoginPlugin\Model\StrategyInterface` interface and is tagged with
`force_customer_login.url_strategy` in your service configuration.

## Tests

You can run the unit tests with the following command (requires dependency installation):
Expand Down
11 changes: 11 additions & 0 deletions src/BitExpertSyliusForceCustomerLoginPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@

namespace BitExpert\SyliusForceCustomerLoginPlugin;

use BitExpert\SyliusForceCustomerLoginPlugin\DependencyInjection\Compiler\RegisterDoctrineMiddlewareCompilerPass;
use BitExpert\SyliusForceCustomerLoginPlugin\DependencyInjection\Compiler\RegisterDoctrineTypeCompilerPass;
use Sylius\Bundle\CoreBundle\Application\SyliusPluginTrait;
use Sylius\Bundle\ResourceBundle\AbstractResourceBundle;
use Sylius\Bundle\ResourceBundle\SyliusResourceBundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;

final class BitExpertSyliusForceCustomerLoginPlugin extends AbstractResourceBundle
{
Expand All @@ -26,4 +29,12 @@ public function getSupportedDrivers(): array
SyliusResourceBundle::DRIVER_DOCTRINE_ORM,
];
}

public function build(ContainerBuilder $container): void
{
parent::build($container);

$container->addCompilerPass(new RegisterDoctrineTypeCompilerPass());
$container->addCompilerPass(new RegisterDoctrineMiddlewareCompilerPass());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/*
* This file is part of the Sylius Force Customer Login package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace BitExpert\SyliusForceCustomerLoginPlugin\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

class RegisterDoctrineMiddlewareCompilerPass implements CompilerPassInterface
{
/**
* @inheritDoc
*/
public function process(ContainerBuilder $container): void
{
$configDefinition = $container->getDefinition('doctrine.dbal.default_connection.configuration');

$middlewareMethodCallArgs = $this->extractMethodCallArgs($configDefinition);
$middlewareMethodCallArgs[0] = array_merge(
$middlewareMethodCallArgs[0] ?? [],
[new Reference('bitexpert.sylius_force_customer_login_plugin.doctrine.middleware')],
);

$configDefinition
->removeMethodCall('setMiddlewares')
->addMethodCall('setMiddlewares', $middlewareMethodCallArgs);
}

/** @return Reference[] */
private function extractMethodCallArgs(Definition $definition): array
{
foreach ($definition->getMethodCalls() as $methodCall) {
if ('setMiddlewares' === $methodCall[0]) {
return $methodCall[1];
}
}

return [];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/*
* This file is part of the Sylius Force Customer Login package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace BitExpert\SyliusForceCustomerLoginPlugin\DependencyInjection\Compiler;

use BitExpert\SyliusForceCustomerLoginPlugin\Doctrine\DBAL\Types\Strategy;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class RegisterDoctrineTypeCompilerPass implements CompilerPassInterface
{
private const CONTAINER_TYPES_PARAMETER = 'doctrine.dbal.connection_factory.types';

/**
* @inheritDoc
*/
public function process(ContainerBuilder $container): void
{
if (!$container->hasParameter(self::CONTAINER_TYPES_PARAMETER)) {
return;
}

$typeDefinition = $container->getParameter(self::CONTAINER_TYPES_PARAMETER);
if (!isset($typeDefinition[Strategy::NAME])) {
$typeDefinition[Strategy::NAME] = ['class' => Strategy::class];
}

$container->setParameter(self::CONTAINER_TYPES_PARAMETER, $typeDefinition);
}
}
65 changes: 65 additions & 0 deletions src/Doctrine/DBAL/Types/Strategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/*
* This file is part of the Sylius Force Customer Login package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace BitExpert\SyliusForceCustomerLoginPlugin\Doctrine\DBAL\Types;

use BitExpert\SyliusForceCustomerLoginPlugin\Model\StrategyInterface;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;

class Strategy extends Type
{
public const NAME = 'Strategy';

/** @var StrategyInterface[] */
private array $strategies = [];

public function getName()
{
return self::NAME;
}

/**
* @param StrategyInterface[] $strategies
*/
public function setStrategies(array $strategies): void
{
$this->strategies = $strategies;
}

public function getSQLDeclaration(array $column, AbstractPlatform $platform)
{
return $platform->getStringTypeDeclarationSQL($column);
}

public function convertToPHPValue($value, AbstractPlatform $platform)
{
if (!is_string($value)) {
return parent::convertToPHPValue($value, $platform);
}

if (isset($this->strategies[$value])) {
return $this->strategies[$value];
}

throw new \RuntimeException(sprintf('Unsupported strategy "%s"!', $value));
}

public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if (!($value instanceof StrategyInterface)) {
return parent::convertToDatabaseValue($value, $platform);
}

return $value->getType();
}
}
48 changes: 48 additions & 0 deletions src/Doctrine/StrategyTypeConfigurationMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* This file is part of the Sylius Force Customer Login package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace BitExpert\SyliusForceCustomerLoginPlugin\Doctrine;

use BitExpert\SyliusForceCustomerLoginPlugin\Doctrine\DBAL\Types\Strategy;
use BitExpert\SyliusForceCustomerLoginPlugin\Model\StrategyInterface;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Types\Type;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;

class StrategyTypeConfigurationMiddleware implements Middleware
{
/**
* @param RewindableGenerator $strategies
*/
public function __construct(private readonly iterable $strategies)
{
}

public function wrap(Driver $driver): Driver
{
$strategies = [];
foreach ($this->strategies as $strategy) {
/** @var StrategyInterface $strategy */
$strategies[$strategy->getType()] = $strategy;
}

try {
/** @var Strategy $type */
$type = Type::getType(Strategy::NAME);
$type->setStrategies($strategies);
} catch (\Exception $e) {
}

return $driver;
}
}
27 changes: 19 additions & 8 deletions src/Form/Type/WhitelistEntryType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,35 @@

namespace BitExpert\SyliusForceCustomerLoginPlugin\Form\Type;

use BitExpert\SyliusForceCustomerLoginPlugin\Model\NegatedRegexMatcher;
use BitExpert\SyliusForceCustomerLoginPlugin\Model\RegexMatcher;
use BitExpert\SyliusForceCustomerLoginPlugin\Model\StaticMatcher;
use BitExpert\SyliusForceCustomerLoginPlugin\Model\StrategyInterface;
use Sylius\Bundle\ChannelBundle\Form\Type\ChannelChoiceType;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

class WhitelistEntryType extends AbstractResourceType
{
/**
* @param RewindableGenerator $strategies
*/
public function __construct(
private readonly iterable $strategies,
string $dataClass,
array $validationGroups = [],
) {
parent::__construct($dataClass, $validationGroups);
}

public function buildForm(FormBuilderInterface $builder, array $options): void
{
$strategies = [];
foreach ($this->strategies as $strategy) {
/** @var StrategyInterface $strategy */
$strategies[] = $strategy;
}

$builder
->add('channels', ChannelChoiceType::class, [
'multiple' => true,
Expand All @@ -41,11 +56,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'empty_data' => '',
])
->add('strategy', ChoiceType::class, [
'choices' => [
new StaticMatcher(),
new RegexMatcher(),
new NegatedRegexMatcher(),
],
'choices' => $strategies,
'choice_value' => 'name',
'choice_label' => function (?StrategyInterface $strategy): string {
return is_object($strategy) ? $strategy->getName() : '';
Expand Down
22 changes: 3 additions & 19 deletions src/Model/WhitelistEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class WhitelistEntry implements WhitelistEntryInterface

private string $urlRule;

private string $strategy;
private StrategyInterface $strategy;

public function __construct()
{
Expand Down Expand Up @@ -85,27 +85,11 @@ public function setUrlRule(string $urlRule): void

public function getStrategy(): StrategyInterface
{
$staticMatcher = new StaticMatcher();
$regexMatcher = new RegexMatcher();
$negatedRegexMatcher = new NegatedRegexMatcher();

if ($this->strategy === $staticMatcher->getType()) {
return $staticMatcher;
}

if ($this->strategy === $regexMatcher->getType()) {
return $regexMatcher;
}

if ($this->strategy === $negatedRegexMatcher->getType()) {
return $negatedRegexMatcher;
}

throw new \RuntimeException('Unsupported strategy!');
return $this->strategy;
}

public function setStrategy(StrategyInterface $strategy): void
{
$this->strategy = $strategy->getType();
$this->strategy = $strategy;
}
}
2 changes: 1 addition & 1 deletion src/Resources/config/doctrine/model/WhitelistEntry.orm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@

<field name="urlRule" type="string" column="url_rule" nullable="false" />

<field name="strategy" type="string" column="strategy" nullable="false" />
<field name="strategy" type="Strategy" column="strategy" nullable="false" />
</mapped-superclass>
</doctrine-mapping>
3 changes: 2 additions & 1 deletion src/Resources/config/services.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>

<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">
<imports>
<import resource="services/doctrine.xml" />
<import resource="services/form.xml" />
<import resource="services/menus.xml" />
<import resource="services/security.xml" />
<import resource="services/strategies.xml" />
</imports>
</container>
11 changes: 11 additions & 0 deletions src/Resources/config/services/doctrine.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<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="bitexpert.sylius_force_customer_login_plugin.doctrine.middleware" class="BitExpert\SyliusForceCustomerLoginPlugin\Doctrine\StrategyTypeConfigurationMiddleware">
<argument type="tagged_iterator" tag="force_customer_login.url_strategy"/>
</service>
</services>
</container>
1 change: 1 addition & 0 deletions src/Resources/config/services/form.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<services>
<service id="BitExpert\SyliusForceCustomerLoginPlugin\Form\Type\WhitelistEntryType">
<argument type="tagged_iterator" tag="force_customer_login.url_strategy"/>
<argument>BitExpert\SyliusForceCustomerLoginPlugin\Model\WhitelistEntry</argument>
<tag name="form.type" />
</service>
Expand Down
19 changes: 19 additions & 0 deletions src/Resources/config/services/strategies.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<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="BitExpert\SyliusForceCustomerLoginPlugin\Model\NegatedRegexMatcher">
<tag name="force_customer_login.url_strategy" />
</service>

<service id="BitExpert\SyliusForceCustomerLoginPlugin\Model\RegexMatcher">
<tag name="force_customer_login.url_strategy" />
</service>

<service id="BitExpert\SyliusForceCustomerLoginPlugin\Model\StaticMatcher">
<tag name="force_customer_login.url_strategy" />
</service>
</services>
</container>
Loading