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

Changing a resource does not clear the cache for all the transforms #67

Closed
ajoliveau opened this issue Jan 17, 2024 · 8 comments
Closed
Assignees

Comments

@ajoliveau
Copy link

ajoliveau commented Jan 17, 2024

Running on Craft 4.5.11.1 with the Cloudflare pluggin v2.0.2

I set the plugin up to clear the Cloudflare cache when a resource is updated. The main resource image is correctly purged, but all the transforms (crops, conversion, etc) is not.
I usually create transforms to have images available as webp, to resize them so they're used in srcsets, and to crop them depending on the focal point.

I shoud specify that they are not named assets, they're dynamically generated at runtime in Twig.
So if my "source" asset's url is uploads/spectacles/show_-NWI_fEElSZn3sx6wBmf_-full.jpg, I have transforms named like uploads/spectacles/_600x450_crop_center-center_85_none/2378715/show_-NWI_fEElSZn3sx6wBmf_-full.webp

If I go to the Utilities and manually purge the transform url, it works correctly.

I can try to build my own purging logic using your API methods, but shouldn't this be part of the resource purging feature ?

image

@ajoliveau ajoliveau added the bug label Jan 17, 2024
@bencroker
Copy link
Contributor

bencroker commented Jan 17, 2024

I understand how you might think this should be automatically handled by the plugin, but it has no way of knowing about every existing image transform, especially since they can be create on-the-fly in the templates, so I’m not sure how to best handle this.

@ajoliveau
Copy link
Author

Yep I understand it's a specific use-case, but is this one that is really so rare ?
I actually researched this, and it seems that Craft actually keeps track of all the transforms, even unnamed ones, in a specific DB table called imagetransformindex

image

Craft has a built-in logic to, when an asset is changed or deleted, find all the transforms associated to this asset and delete them in the filesystem. You can see the method that's called here : https://docs.craftcms.com/api/v4/craft-imagetransforms-imagetransformer.html#method-getallcreatedtransformsforasset

Unfortunately all this logic is private, and there's no way to reuse their functions and methods, but what I managed to do is to listen on the EVENT_BEFORE_INVALIDATE_ASSET_TRANSFORMS event, and manually copy all the code to get the list of transforms when Craft would delete them. Then I just call purgeUrls() on them.

Here is the code inside my module :

Event::on(
    ImageTransforms::class,
    ImageTransforms::EVENT_BEFORE_INVALIDATE_ASSET_TRANSFORMS,
    function($event) {
        $asset = $event->asset;
        $urlsToInvalidate = [];

        $transformIndexes = (new Query())
        ->select([
            'filename',
            'transformString',
        ])
        ->from([Table::IMAGETRANSFORMINDEX])
        ->where(['assetId' => $asset->id])
        ->all();

        $rootUrl = rtrim($asset->getVolume()->getTransformFs()->getRootUrl(), '/');
        $basePath = $asset->getVolume()->transformSubpath;
        $basePath = StringHelper::removeRight($basePath, '/');
        $basePath = ($basePath ? $basePath . DIRECTORY_SEPARATOR : '') . $asset->folderPath;

        foreach ($transformIndexes as $key => $transformIndex) {

            $subfolder = $transformIndex['transformString'];
            if (!empty($transformIndex['filename']) && $transformIndex['filename'] !== $asset->getFilename()) {
                $subfolder .= DIRECTORY_SEPARATOR . $asset->id;
            }

            $filename = $transformIndex['filename'] ?: $asset->getFilename();

            $path = $basePath . DIRECTORY_SEPARATOR . $subfolder . DIRECTORY_SEPARATOR . $filename;
            
            $url = $rootUrl . $path;

            $urlsToInvalidate[] = $url;
        }
        
        $return = Cloudflare::getInstance()->api->purgeUrls($urlsToInvalidate);
        Craft::info("Purged urls : ", 'custom-cf-purge');
        Craft::info($urlsToInvalidate, 'custom-cf-purge');
        Craft::info($return, 'custom-cf-purge');

    }    
);

It seems to work fine ! I still think this feature would be inside the scope of the plugin, it's nice to know it's doable at least :)

@bencroker
Copy link
Contributor

You’re right, this does actually seem much more feasible given that Craft tracks all image transforms. Thanks for sharing your code, I’ll look into what it would take to add this to the plugin.

@bencroker bencroker added enhancement and removed bug labels Jan 17, 2024
@ajoliveau
Copy link
Author

Glad to hear that, thank you ! I can turn my code snippet above into a pull request if that can help.

@bencroker
Copy link
Contributor

That’s ok, it’s a good starting point that I can reference but not yet sure how I’d integrate this.

@bencroker bencroker self-assigned this Jan 31, 2024
@bencroker
Copy link
Contributor

So after exploring, it turns out that we’re already doing something similar in Blitz:
https://github.com/putyourlightson/craft-blitz/blob/3d838a8332327c093821ddd5247621ee59dab5a8/src/helpers/SiteUriHelper.php#L291-L312

The main difference is that Blitz is specifically tracking changes to assets based on modified images. So I’ve gone with your suggested event, which takes effect when Assets are selected as purgeable elements, in 9a683fa

You can test this by running composer require "putyourlightson/craft-cloudflare:dev-develop as 2.1.0".

@bencroker
Copy link
Contributor

Have you had a chance to test this yet, @ajoliveau?

@bencroker
Copy link
Contributor

Released in 2.1.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants