Add a feature to a package without forking or hacking

Laravel is so flexible

Joel Clermont
Joel Clermont
2024-05-28

On a recent project, I was helping a team migrate their project from using local storage for file uploads to using Digital Ocean's object storage.

For some of their uploads, they were using the ckfinder/ckfinder-laravel-package to provide a backend for the JS CkFinder library. Thankfully, this package supports S3 as a storage backend, and Digital Ocean's object storage is S3-compatible.

But there was a catch: when the package builds an S3 client to manage the storage interactions, it doesn't pass in the endpoint configuration. Without this, the S3 client is going to try to connect to s3.amazonaws.com, which obviously won't work for Digital Ocean's object storage.

I literally only needed to add a 3-line if block to the S3 client setup to solve this, but that code exists in the vendor folder, and we all know it's a very bad idea to try to patch a package in the vendor folder.

I could have forked the package, made the change, and then required my forked version in the project, but that's a lot of overhead for a 3-line change.

Instead, I opted to use Laravel's service container to my advantage. I saw how the package was using a service provider to register an instance of the primary class, and inside that class were all the available storage backends that it knew about.

All I needed to do was let Laravel boot that service provider, register the configured instance of that class, then I could come in right after, grab that instance, and insert my Digital Ocean-aware storage backend.

Here's a simplified version of what I did:

// in AppServiceProvider (or any service provider)
public function boot(): void
{
    $this->app->extend('ckfinder.connector', function (CkFinder $service) {
        $backendFactory = $service->getBackendFactory();

        $backendFactory->registerAdapter('digitalocean', function ($backendConfig) use ($backendFactory) {
            // my modified version of the s3 adapter which supports the endpoint config
        });

        return $service;
    });
}

This is a much nicer solution than hacking vendor code or forking a package. Laravel is so flexible!

Here to help,

Joel

P.S. Want a second set of eyes on something you're working on? Aaron and I both offer Laravel pairing sessions to work on whatever you need help with.

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼

Level up your Laravel skills!

Each 2-minute email has real-world advice you can use.