Flush content cache when assets is added to tag or assetCollection

Hey guys

Inspired by https://github.com/daniellienert/DL.Gallery (thanks to @daniellienert) I build a gallery node type where the editor can select image-assets via asset-tag or asset-collection and displays the all images as gallery.
Works very well.

But… when the editor adds/removes an image to an asset-collection (in the media module), the gallery nodes that show images of that asset-collection are not updated. They still display the “old” set of images.
I assume the content cache for my gallery node needs to be flushed.
So I looked at http://neos.readthedocs.io/en/stable/CreatingASite/ContentCache.html#cache-entry-tags if there is a cache entry tag for that purpose, but looks like there is none.

My next steps would be:

  1. Annotate my gallery-node fusion object with @cache and specify an entry-tag "${‘AssetCollection’ + node.assetCollectionIdentifier} (not sure how to get the assetCollectionIdentifier via eel yet)
  2. Create an aspect that “hooks” into several media-package methods and flush the contentCache for the tag “AssetCollection”+AssetCollectionIdentifier

The methods where the aspect hooks in would be:

  • \Neos\Media\Domain\Model\AssetCollection::addAsset
  • \Neos\Media\Domain\Model\AssetCollection::removeAsset
  • \Neos\Media\Domain\Model\AssetCollection::setAssets
  • \Neos\Media\Domain\Model\Asset::addTag
  • \Neos\Media\Domain\Model\Asset::removeTag
  • \Neos\Media\Domain\Model\Asset::setTags

So, my question is: is this the right way or is there a better solution? Maybe I miss an already exisiting solution?

Best regards,
Peter

Hi Peter,

Sounds about right. Unfortunately there’s no easy signals to plug into for when an asset or asset collection is updated.

There’s one more aspect you might need to hook into, which is \Neos\Media\Domain\Model\Asset::setAssetCollections.

An alternative way to hook in without using AOP is to use Doctrine listeners to monitor those models and act when they’re inserted, updated or removed. This way you also ensure you update your content when an asset or collection is deleted.

See https://github.com/neos/neos-development-collection/blob/master/Neos.Neos/Configuration/Settings.yaml#L417 for an example how that can be done.
Also http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html

Hope that helps.

Cheers,
Aske

Thanks Aske, I will try out the listeners. Sounds to be a good way. Will post my solution here once done, this will happen by next week I think.

Any hint how to get the identifier of an object (like assetCollection) in with eel?
${‘AssetCollection’ + node.assetCollectionIdentifier}

I think I need to ask the persistenceManager for it, right?

You’re welcome.

Well if you’re using data sources for your select editors to choose tags/collections, they should provide you with that identifier?

@aertmann: What do you think about extending the core ContentCacheFlusher with registerAssetTagChange and registerAssetCollectionChange in order to flush the cache tags AssetTag_<TAGIDENTIFIER> and ``AssetCollcetion_<COLLECTIONIDENTIFIER`

Not sure to be honest. Adding assets by collection is not a core feature, however updating content when an asset is updated could be useful in case you use the caption as a title or s.th. Guess it wouldn’t hurt having it in the core, however currently the core doesn’t need it as nothing depends on it.

Also if adding those methods, triggering them should also be in the core I think.

I found that \Neos\Neos\Fusion\Cache\ContentCacheFlusher::registerAssetChange clears the content-cache of all nodes that reference a changed asset. This is done in that method by getting all nodes with the help of \Neos\Media\Domain\Service\AssetService::getUsageReferences.

I looked inside \Neos\Neos\Domain\Strategy\AssetUsageInNodePropertiesStrategy::getRelatedNodes and found that it is very easy to extend this strategy to also find related nodes by checking the collections/tags.

Just a few lines needed:

if ($asset instanceof Asset) {
        //add relationIdentifiers for assetCollections of this asset
        $relationIdentifiers = [];
        foreach ($asset->getAssetCollections() as $assetCollection) {
            $relationIdentifiers[] = $this->persistenceManager->getIdentifierByObject($assetCollection);
        }
        $relationMap[AssetCollection::class] = $relationIdentifiers;

        //add relationIdentifiers for tags of this asset
        $relationIdentifiers = [];
        foreach ($asset->getTags() as $tag) {
            $relationIdentifiers[] = $this->persistenceManager->getIdentifierByObject($tag);
        }
        $relationMap[Tag::class] = $relationIdentifiers;
    }

I did that with an aspect to make it work as long there is nothing for this in the core.

The result is:

  • ContentCache for nodes is flushed when adding/removing assets to/from collections/tags
  • Also the usage-count (and usage-list) for the asset shows those nodes. I like it but it may tbd if those “indirect” usage also should count
  • I dont need any cache-annotation in my fusion-objects as the ContentCacheFlusher takes care of everything

If you core-team guys like this idea, I would open a MR and put in the chanes from my aspect directly in the AssetUsageInNodePropertiesStrategy-Class.

From my perspective I think it very nice to be able to not only reference assets in nodes but also tags or assetCollections. I don’t think I am the only one who likes to use those references. My usecases are gallery-nodes and assetLists where the editor can easily work with collections instead of assets directly.
I would also love to have https://github.com/daniellienert/DL.Gallery/blob/master/Classes/DataSources/AssetCollectionDataSource.php and https://github.com/daniellienert/DL.Gallery/blob/master/Classes/DataSources/TagDataSource.php in the core. This would provide all tools for an integrator to build nodeTypes and do those references without the need of AOP or PHP-code at all.

Would love to hear your opinions about that, guys.

Hey Peter,

that is a nice solution to your problem!

My suggestion would be to not use AOP for this as AOP is an unplanned change to the core and might break on next version update. A better solution would be to extend the asset usage detection by doing your own implementation of the AssetUsageStrategyInterface - all classes implementing this interface are taken into account when the asset usage calculations are done.

Need to think about if this is a good core feature / behavior, as sometimes the lose coupling from an asset through a tag to a node without usage detection and deletion-blocking has its advantages.

I am really hoping this can get into the core and my aspect was aiming in that direction. If it will not happen that you guys like that to be in the core then I agree that an extra UsageStrategy is better than an aspect. Just feels strange to me to copy that code in almost every Neos project as our editors really wanna work with collections.