Buy
Buy

Using the Filesystem

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Config done! Let's get to work in UploaderHelper. Instead of passing the $uploadsPath, which we were using to store things, change this to FilesystemInterface - the one from Flysystem - $filesystem. Use that below, and rename the property to $filesystem.

... lines 1 - 5
use League\Flysystem\FilesystemInterface;
... lines 7 - 10
class UploaderHelper
{
... lines 13 - 14
private $filesystem;
... lines 16 - 18
public function __construct(FilesystemInterface $filesystem, RequestStackContext $requestStackContext)
{
$this->filesystem = $filesystem;
$this->requestStackContext = $requestStackContext;
}
... lines 24 - 47
}

Now, in the method, instead of $file->move(), we can say $this->filesystem->write(), which is used to create new files. Pass this self::ARTICLE_IMAGE.'/'.$newFilename and then the contents of the file: file_get_contents() with $file->getPathname().

... lines 1 - 10
class UploaderHelper
{
... lines 13 - 24
public function uploadArticleImage(File $file): string
{
... lines 27 - 33
$this->filesystem->write(
self::ARTICLE_IMAGE.'/'.$newFilename,
file_get_contents($file->getPathname())
);
... lines 38 - 39
}
... lines 41 - 47
}

That's it! This File object has a ton of different methods for getting the filename, the full path, the file without the extension and more. Honestly, I get them all confused and have to Google them. getPathname() gives us the absolute file path on the filesystem.

Above, we can get rid of the unused $destination variable. Because the filesystem's root is public/uploads/, the only thing we need to pass to write() is the path relative to that: article_image/ and then $newFilename.

I think we're ready! Let's clear out the uploads/ directory again. And then try our fixtures:

php bin/console doctrine:fixtures:load

Oh! It does not work!

Binding the Filesystem for Autowiring

Unused binding $uploadsPath in service UniqueUserValidator.

This is a bad error message from Symfony, at least the second half of the message. A minute ago, we had an argument here called $uploadsPath. Open up config/services.yaml. Ah, that worked because we have $uploadsPath configured as a global bind. And when you configure a bind, it must be used in at least one place in your app. If it's not used anywhere, you get this error. It's kinda nice: Symfony is saying:

Hey! You configured this bind... but you're not using it - are you maybe... messing something up on accident?

The UniqueUserValidator part of the message is really a bug in the error message, which makes this a bit confusing.

Anyways, remove that bind and try the fixtures again:

php bin/console doctrine:fixtures:load

This is the error I was waiting for.

Cannot autowire service UploaderHelper argument $filesystem of __construct() references FilesystemInterface but no such service exists.

There are two ways to fix this. First, we could re-add the alias option and point it at this FilesytemInterface. Or, we can create a new bind. I'll do the second, because it works better if you have multiple filesystem services, which we will soon. First, rename the argument to be more descriptive, how about $publicUploadFilesystem.

... lines 1 - 10
class UploaderHelper
{
... lines 13 - 18
public function __construct(FilesystemInterface $publicUploadsFilesystem, RequestStackContext $requestStackContext)
{
$this->filesystem = $publicUploadsFilesystem;
... line 22
}
... lines 24 - 47
}

Then, under bind, set $publicUploadFilesystem to the filesystem service id - you can see it in the error. It suggests two services that implement the FilesystemInterface type-hint - we want the second one. Type @ then paste.

... lines 1 - 9
services:
... lines 11 - 19
bind:
... lines 21 - 22
$publicUploadsFilesystem: '@oneup_flysystem.public_uploads_filesystem_filesystem'
... lines 24 - 48

One more time for the fixtures!

php bin/console doctrine:fixtures:load

Ok, no error! Check out the public/uploads/ directory. Yes! We have files! Refresh the homepage. We are good! We still need to tweak a few more details, but our app is now way more ready to work locally or in the cloud.

Leave a comment!