3. Usage¶
3.1. How to Create and Register Custom Rule Applicator¶
Rule applicators are used to apply given rule’s configuration to an object. Read more about it in the Rule component documentation (in the Usage section).
3.1.1. Creating the Rule Applicator Class¶
A new Rule Applicator has to implement the SWP\Component\Rule\Applicator\RuleApplicatorInterface
interface.
ArticleRuleApplicator
class example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | <?php
namespace Acme\DemoBundle\Applicator;
// ..
use Psr\Log\LoggerInterface;
use SWP\Bundle\ContentBundle\Model\ArticleInterface;
use SWP\Bundle\ContentBundle\Provider\RouteProviderInterface;
use SWP\Component\Rule\Applicator\RuleApplicatorInterface;
use SWP\Component\Rule\Model\RuleSubjectInterface;
use SWP\Component\Rule\Model\RuleInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class ArticleRuleApplicator implements RuleApplicatorInterface
{
/**
* @var RouteProviderInterface
*/
private $routeProvider;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var array
*/
private $supportedKeys = ['route', 'templateName'];
/**
* ArticleRuleApplicator constructor.
*
* @param RouteProviderInterface $routeProvider
* @param LoggerInterface $logger
*/
public function __construct(RouteProviderInterface $routeProvider, LoggerInterface $logger)
{
$this->routeProvider = $routeProvider;
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function apply(RuleInterface $rule, RuleSubjectInterface $subject)
{
$configuration = $this->validateRuleConfiguration($rule->getConfiguration());
if (!$this->isAllowedType($subject) || empty($configuration)) {
return;
}
/* @var ArticleInterface $subject */
if (isset($configuration[$this->supportedKeys[0]])) {
$route = $this->routeProvider->getOneById($configuration[$this->supportedKeys[0]]);
if (null === $route) {
$this->logger->warning('Route not found! Make sure the rule defines an existing route!');
return;
}
$subject->setRoute($route);
}
$subject->setTemplateName($configuration[$this->supportedKeys[1]]);
$this->logger->info(sprintf(
'Configuration: "%s" for "%s" rule has been applied!',
json_encode($configuration),
$rule->getExpression()
));
}
/**
* {@inheritdoc}
*/
public function isSupported(RuleSubjectInterface $subject)
{
return $subject instanceof ArticleInterface && 'article' === $subject->getSubjectType();
}
private function validateRuleConfiguration(array $configuration)
{
$resolver = new OptionsResolver();
$this->configureOptions($resolver);
try {
return $resolver->resolve($configuration);
} catch (\Exception $e) {
$this->logger->warning($e->getMessage());
}
return [];
}
private function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([$this->supportedKeys[1] => null]);
$resolver->setDefined($this->supportedKeys[0]);
}
private function isAllowedType(RuleSubjectInterface $subject)
{
if (!$subject instanceof ArticleInterface) {
$this->logger->warning(sprintf(
'"%s" is not supported by "%s" rule applicator!',
is_object($subject) ? get_class($subject) : gettype($subject),
get_class($this)
));
return false;
}
return true;
}
}
|
3.1.2. Configuring the Rule Applicator¶
To register your new rule applicator, simply add a definition to your services file and tag it with a special name: applicator.rule_applicator
, it will be automatically added to the chain of rule applicators:
1 2 3 4 5 6 7 8 | # Resources/config/services.yml
acme_my_custom_rule_applicator:
class: 'Acme\DemoBundle\Applicator\ArticleRuleApplicator'
arguments:
- '@swp.provider.route'
- '@logger'
tags:
- { name: applicator.rule_applicator }
|
3.2. How to Create and Enable Custom Rule Entity¶
In some cases you would want to extend the default Rule
model to add some extra properties etc.
To do this you need to create a custom class which extends the default one.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php
// ..
namespace Acme\DemoBundle\Entity;
use SWP\Component\Rule\Model\Rule as BaseRule;
class Rule extends BaseRule
{
protected $something;
public function getSomething()
{
return $this->something;
}
public function setSomething($something)
{
$this->something = $something;
}
}
|
Add class’s mapping file:
1 2 3 4 5 6 7 | # Acme\DemoBundle\Resources\config\doctrine\Rule.orm.yml
Acme\DemoBundle\Entity\Rule:
type: entity
table: custom_rule
fields:
something:
type: string
|
The newly created class needs to be now added to the bundle’s configuration:
1 2 3 4 5 6 7 8 | # app/config/config.yml
swp_rule:
persistence:
orm:
# ..
classes:
rule:
model: Acme\DemoBundle\Entity\Rule
|
That’s it, a newly created class will be used instead.
Note
You could also provide your own implementation for Rule Factory and Rule Repository. To find out more about it check How to automatically register Services required by the configured Storage Driver
3.3. How rules are processed?¶
You can create Event Subscriber which can listen on whatever event is defined. If the event is dispatched, the subscriber should run Rule Processor which will process all rules.
Example subscriber:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <?php
namespace SWP\Bundle\ContentBundle\EventListener;
use SWP\Bundle\ContentBundle\ArticleEvents;
use SWP\Bundle\ContentBundle\Event\ArticleEvent;
use SWP\Component\Rule\Processor\RuleProcessorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ProcessArticleRulesSubscriber implements EventSubscriberInterface
{
/**
* @var RuleProcessorInterface
*/
private $ruleProcessor;
/**
* ProcessArticleRulesSubscriber constructor.
*
* @param RuleProcessorInterface $ruleProcessor
*/
public function __construct(RuleProcessorInterface $ruleProcessor)
{
$this->ruleProcessor = $ruleProcessor;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
ArticleEvents::PRE_CREATE => 'processRules',
];
}
/**
* @param ArticleEvent $event
*/
public function processRules(ArticleEvent $event)
{
$this->ruleProcessor->process($event->getArticle());
}
}
|
3.4. Enabling separate Monolog channel¶
It is possible to enable a separate Monolog channel to which all Rule Bundle related logs will be forwarded. An example log entry might be logged when the rule can not be evaluated properly etc. You could have then a separate log file for (which will log everything related to that bundle) which will be saved under the directory app/logs/
in your application and will be named, for example: swp_rule_<env>.log
. By default, a separate channel is not enabled. You can enable it by adding an extra channel in your Monolog settings (in one of your configuration files):
1 2 3 4 5 6 7 8 | # app/config/config.yml
monolog:
handlers:
swp_rule:
level: debug
type: stream
path: '%kernel.logs_dir%/swp_rule_%kernel.environment%.log'
channels: swp_rule
|
For more details see the Monolog documentation.