spacecatninja / craft-imager-x

Image transforms, optimizations and manipulations for your Craft CMS site.
Other
26 stars 16 forks source link

Problems creating a custom External Storage #261

Closed ax2000 closed 5 months ago

ax2000 commented 5 months ago

I'm submitting a...

I'm trying to create a custom external source via custom module following the instructions under the Extending section in the docs.

I'm sure I'm missing something but somehow it seems that the event EVENT_REGISTER_EXTERNAL_STORAGES never gets called from within the module as per below:

use spacecatninja\imagerx\ImagerX;
use spacecatninja\imagerx\events\RegisterExternalStoragesEvent;
...
Event::on(
            ImagerX::class,
            ImagerX::EVENT_REGISTER_EXTERNAL_STORAGES,
            function (RegisterExternalStoragesEvent $event) {
                dd($event);
                $event->storages['azure'] = ImagerBlobService::class;
            }
        );

The dump and die never gets printed in the screen.

Then I've got the following in the imager-x.php config file

<?php
use craft\helpers\App;
return [
    'imagerUrl' => App::env('AZURE_BASE_URL_IMAGES') . '/imager',
    'storages' => ['azure'],
    'storageConfig' => [
        'azure'  => [
            'endpoint' => App::env('AZURE_BASE_URL_IMAGES'),
            'connectionString' => App::env('AZURE_CONNECTION_STRING'),
            'container' => App::env('AZURE_CONTAINER_IMAGES'),
            'folder' => 'imager',
            'requestHeaders' => array(),
        ]
    ],
];

When parsing the image in the frontend I'm getting Could not find a registered storage with handle "azure".

Screenshot 2024-04-30 at 11 42 04

Any pointers would be greatly appreciated.

Additional info

aelvan commented 5 months ago

Hi, Your code looks correct. Are you sure that your module is initialized correctly? If you put a dd in front of the listener, does that trigger?

ax2000 commented 5 months ago

Thanks for checking this @aelvan .

The module seems to initialise just fine. I've got a bunch of other listeners for some custom features and they all work as expected.

I put a dd in front of the listener as you requested and it worked as expected as well.

If you can't replicate on your end, do you think it might be another plugin getting on the way?

I'll try and disable some plugins and also comment all the other module event listeners to see if its environment related and will let you know.

aelvan commented 5 months ago

I'll do some additional testing and get back to you.

ax2000 commented 5 months ago

Thanks for that @aelvan .

I have commented all extra logic in my custom module and also disabled all plugins except for imager-x and the dd($event); from my first comment still doesn't parse when within the EVENT_REGISTER_EXTERNAL_STORAGES event listener.

I can't see any plugin interfering but maybe I'm still missing something. Have you managed to replicate from your end by any chance?

aelvan commented 5 months ago

Hi again,

I've tested thoroughly now, but I'm not able to replicate. I thought maybe there could be a race condition going on, but it seems like Yii handles that, which was what I originally thought. I can't replicate the dd not working either, at my end it always work inside the event handler.

Are you running in devMode while testing this, or could there we some errors being swallowed by PHP? Are you 100% sure that the pro version of Imager is activated? You should get some other error then, but. What happens if you try to register the storage directly like this:

ImagerService::registerExternalStorage('azure', ImagerBlobService::class);
ax2000 commented 5 months ago

Hi @aelvan ,

Thanks very much for testing this.

To answer your questions:

Screenshot 2024-05-07 at 18 38 52

To see if it was an issue with my codebase, I have created a blank new project with no plugins installed except for imagerx and the issue seems to persist there as well. Maybe it's related with the ddev environment?

My module looks like

<?php

namespace modules\lilydale;

use Craft;
use spacecatninja\imagerx\events\RegisterExternalStoragesEvent;
use spacecatninja\imagerx\ImagerX;
use yii\base\Event;
use yii\base\Module as BaseModule;

/**
 * lilydale module
 *
 * @method static Module getInstance()
 */
class Module extends BaseModule
{
    public function init(): void
    {
        Craft::setAlias('@modules/lilydale', __DIR__);

        // Set the controllerNamespace based on whether this is a console or web request
        if (Craft::$app->request->isConsoleRequest) {
            $this->controllerNamespace = 'modules\\lilydale\\console\\controllers';
        } else {
            $this->controllerNamespace = 'modules\\lilydale\\controllers';
        }

        parent::init();

        // Defer most setup tasks until Craft is fully initialized
        Craft::$app->onInit(function() {
            $this->attachEventHandlers();
        });
    }

    private function attachEventHandlers(): void
    {
        Event::on(
            ImagerX::class,
            ImagerX::EVENT_REGISTER_EXTERNAL_STORAGES,
            function (RegisterExternalStoragesEvent $event) {
               dd("foo");
                $event->storages['azure'] = ImagerBlobService::class;
            }
        );
    }
}

The dd never gets called though. I checked and it does get called outside of the event listener.

The fresh install has:

Not sure if the above will help but let me know how you go and if you'd need me to provide anything else.

aelvan commented 5 months ago

Right, now I can reproduce. πŸŽ‰

And the solution is simple, you must attach the event handler before Craft::$app->onInit(), this is not one of those most setup tasks that I see that the Craft documentation recommends now. πŸ™‚

The reason for this is that Imager needs to init storages (and the other components that can be extended), during init. More specifically, it happens during Plugins::EVENT_AFTER_LOAD_PLUGINS, which ensures that all modules and plugins are loaded before triggering the event. The code that you put in onInit though happens afterwards, so it's too late and the event handler is never run.

I'll make sure to update the documentation for this.

ax2000 commented 5 months ago

Thanks @aelvan , it does make sense now!

I've added the listener before the onInit() and it works as expected.

Now I'll need to polish the logic for Azure. Hopefully I'll get it working following your built in storages but I might open a ticket if I get stuck. πŸ™‚

Thanks heaps!

aelvan commented 5 months ago

Great. Have you looked at https://plugins.craftcms.com/imager-x-azure-blob ? It's for Craft 3, but in terms of the interface with Imager X, nothing should have changed. In terms of the interface with Azure I don't know if this is what you're looking for, not familiar with that eco system, but maybe worth a look. :)

ax2000 commented 5 months ago

Yep - that's exactly what I did.

It does seem to work just fine already πŸŽ‰