Skip to content

Commit

Permalink
implement inherited element dispatcher, resolves #42
Browse files Browse the repository at this point in the history
  • Loading branch information
solverat committed Dec 17, 2021
1 parent 49d95df commit 06cc212
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 9 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -66,6 +66,7 @@ $ bin/console dynamic-search:run -v
![image](https://user-images.githubusercontent.com/700119/146414238-ad2964e6-e873-4607-a89b-bc2ec2e5b95c.png)

- [Example Setup](docs/0_ExampleSetup.md)
- [Dispatch Workflow](docs/01_DispatchWorkflow.md)
- Configuration
- Context Guard
- Document Definition
Expand Down
1 change: 1 addition & 0 deletions UPGRADE.md
Expand Up @@ -29,6 +29,7 @@

### New Features
- Introducing backend panel and HealthState [#34](https://github.com/dachcom-digital/pimcore-dynamic-search/issues/34)
- Provide inherited element dispatcher [#42](https://github.com/dachcom-digital/pimcore-dynamic-search/issues/42)

***

Expand Down
67 changes: 67 additions & 0 deletions docs/01_DispatchWorkflow.md
@@ -0,0 +1,67 @@
## Dispatch Workflow
Initially, you need to create and fill your index by executing the run command:

> Use the `-v flag to output some log information`
```bash
$ bin/console dynamic-search:run -v
```

> There could be more to consider, depending on your data- and/or index provider.
### Listener
If you want to update your index after modifying pimcore elements, you need to enable it first
This tells DynamicSearch to register a dedicated maintenance task:

```yaml
dynamic_search:
enable_pimcore_element_listener: true
```

#### Element Watcher
At every modification/deletion event of every pimcore element,
DynamicSearch will validate this element by calling [ResourceValidator](./40_ResourceValidator.md).
If the resource is still present, DynamicSearch creates an `Envelope` which will be passed to a dedicated queue.

#### Element Processor
Another maintenance task processes that queue (Interval depends on your maintenance cronjob).
If available, the `Envelope` will be submitted to the index provider.

> DynamicSearch will sort envelopes by creation date.
> If you're updating your element multiple times before the element processor kicks in,
> only the latest envelope will be used. This allows us to save some trees.
#### Element Process Command
There is a secret command which allows you to dispatch the queue processor immediately.
This comes in handy if you're debugging your application:

> Use the `-v flag to output some log information`
```bash
$ bin/console dynamic-search:check-queue -v
```

#### Inheritance
Inheritance is unknown to DynamicSearch. If you're updating an object, that very object will be transmitted to the queue.
But in some cases, you're working with variants and those should get updated too.
Lets' enable it:

```yaml
dynamic_search:
element_inheritance:
enabled: true
```

#### Inheritance Dispatch Origin
By default, a real user must be involved to dispatch the inheritance check.
Which means, only if the updated comes from the pimcore backend, the child elements will be added to the queue.
We're doing this to avoid confusion.
If you're updating elements via api, you're most likely handling child elements there, so we don't need to add an extra round.

However, you could disable it to allow queued inherited elements at every state:

```yaml
dynamic_search:
element_inheritance:
origin_dispatch: 'all'
```
7 changes: 7 additions & 0 deletions src/DynamicSearchBundle/DependencyInjection/Configuration.php
Expand Up @@ -17,6 +17,13 @@ public function getConfigTreeBuilder(): TreeBuilder
$rootNode
->children()
->booleanNode('enable_pimcore_element_listener')->defaultFalse()->end()
->arrayNode('element_inheritance')
->addDefaultsIfNotSet()
->children()
->booleanNode('enabled')->defaultFalse()->end()
->enumNode('origin_dispatch')->values(['user', 'all'])->defaultValue('user')->end()
->end()
->end()
->arrayNode('context')
->useAttributeAsKey('name')
->arrayPrototype()
Expand Down
70 changes: 62 additions & 8 deletions src/DynamicSearchBundle/EventListener/PimcoreElementListener.php
Expand Up @@ -5,26 +5,31 @@
use DynamicSearchBundle\Configuration\ConfigurationInterface;
use DynamicSearchBundle\Context\ContextDefinitionInterface;
use DynamicSearchBundle\Queue\DataCollectorInterface;
use Pimcore\Bundle\AdminBundle\Security\User\TokenStorageUserResolver;
use Pimcore\Event\AssetEvents;
use Pimcore\Event\DataObjectEvents;
use Pimcore\Event\DocumentEvents;
use Pimcore\Event\Model\AssetEvent;
use Pimcore\Event\Model\DataObjectEvent;
use Pimcore\Event\Model\DocumentEvent;
use Pimcore\Model\DataObject\Concrete;
use Pimcore\Model\DataObject;
use Pimcore\Model\Element\ElementInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class PimcoreElementListener implements EventSubscriberInterface
{
protected ConfigurationInterface $configuration;
protected DataCollectorInterface $dataCollector;
protected TokenStorageUserResolver $userResolver;

public function __construct(
ConfigurationInterface $configuration,
DataCollectorInterface $dataCollector
DataCollectorInterface $dataCollector,
TokenStorageUserResolver $userResolver
) {
$this->configuration = $configuration;
$this->dataCollector = $dataCollector;
$this->userResolver = $userResolver;
}

public static function getSubscribedEvents(): array
Expand Down Expand Up @@ -84,19 +89,21 @@ public function onObjectPostUpdate(DataObjectEvent $event): void
return;
}

/** @var Concrete $object */
/** @var DataObject\Concrete $object */
$object = $event->getObject();

$dispatchType = method_exists($object, 'isPublished')
? $object->isPublished() === false
? ContextDefinitionInterface::CONTEXT_DISPATCH_TYPE_DELETE
: ContextDefinitionInterface::CONTEXT_DISPATCH_TYPE_UPDATE
: ContextDefinitionInterface::CONTEXT_DISPATCH_TYPE_UPDATE;
$dispatchType = ContextDefinitionInterface::CONTEXT_DISPATCH_TYPE_UPDATE;
if (method_exists($object, 'isPublished') && $object->isPublished() === false) {
$dispatchType = ContextDefinitionInterface::CONTEXT_DISPATCH_TYPE_DELETE;
}

$this->dataCollector->addToGlobalQueue(
$dispatchType,
$event->getObject()
);

$this->checkInheritanceIndex($object);

}

public function onObjectPreDelete(DataObjectEvent $event): void
Expand Down Expand Up @@ -146,4 +153,51 @@ public function onAssetPreDelete(AssetEvent $event): void
$event->getAsset()
);
}

protected function checkInheritanceIndex(ElementInterface $element): void
{
// currently, only objects are allowed.

if (!$element instanceof DataObject\Concrete) {
return;
}

$inheritanceConfiguration = $this->configuration->get('element_inheritance');

if ($inheritanceConfiguration['enabled'] === false) {
return;
}

$classDefinition = DataObject\ClassDefinition::getById($element->getClassId());

if (!$classDefinition instanceof DataObject\ClassDefinition) {
return;
}

if ($classDefinition->getAllowInherit() === false) {
return;
}

// we mostly want to fetch child elements if this comes from a real user (e.g. from backend)
// if user is null we're most probably in a CLI process which handles children/variants by itself!
// this can be changed by set origin_dispatch to "all"
if ($inheritanceConfiguration['origin_dispatch'] === 'user' && $this->userResolver->getUser() === null) {
return;
}

$list = new DataObject\Listing();
$list->setCondition('o_path LIKE ' . $list->quote($element->getRealFullPath() . '/%'));
$list->setUnpublished(false);
$list->setObjectTypes([DataObject\AbstractObject::OBJECT_TYPE_OBJECT, DataObject\AbstractObject::OBJECT_TYPE_VARIANT]);

foreach ($list->getObjects() as $childObject) {
$dispatchType = ContextDefinitionInterface::CONTEXT_DISPATCH_TYPE_UPDATE;
if (method_exists($childObject, 'isPublished') && $childObject->isPublished() === false) {
$dispatchType = ContextDefinitionInterface::CONTEXT_DISPATCH_TYPE_DELETE;
}

$this->dataCollector->addToGlobalQueue($dispatchType, $childObject);
}
}

}
Expand Up @@ -6,7 +6,7 @@

class NormalizedDataResource implements NormalizedDataResourceInterface
{
protected ResourceContainerInterface $resourceContainer;
protected ?ResourceContainerInterface $resourceContainer;
protected ResourceMetaInterface $resourceMeta;

public function __construct(?ResourceContainerInterface $resourceContainer, $resourceMeta)
Expand Down

0 comments on commit 06cc212

Please sign in to comment.