68publishers / file-storage
File management based on Flysystem with an integration into Nette Framework.
Requires
- php: ^8.1
- ext-fileinfo: *
- ext-json: *
- league/flysystem: ^3.12
- psr/log: ^1.1 || ^2.0 || ^3.0
Requires (Dev)
- 68publishers/doctrine-bridge: ^1.0
- doctrine/dbal: ^2.13.1 || ^3.1.1
- fig/log-test: ^1.1
- friendsofphp/php-cs-fixer: ^3.13
- kubawerlos/php-cs-fixer-custom-fixers: ^3.13
- latte/latte: ^3.0
- league/flysystem-memory: ^3.10
- mockery/mockery: ^1.5
- nette/application: ^3.1.8
- nette/bootstrap: ^3.1
- nette/di: ^3.0.10
- nette/tester: ^2.4.3
- phpstan/phpstan: ^1.9
- phpstan/phpstan-nette: ^1.1
- roave/security-advisories: dev-latest
- symfony/console: ^5.0 | ^6.0
Suggests
- 68publishers/doctrine-bridge: For an integration of a custom Doctrine types into the Nette.
- latte/latte: For usage with Latte templates.
- nette/di: For an integration with Nette Framework.
- symfony/console: If you want to use a console commands.
Conflicts
- 68publishers/doctrine-bridge: <1.0
- latte/latte: <3.0
- nette/di: <3.0.10
- nette/schema: <1.1
- symfony/console: <5.0
README
📁 File management based on Flysystem with an integration into Nette Framework.
Installation
The best way to install 68publishers/file-storage is using Composer:
$ composer require 68publishers/file-storage
Integration into Nette Framework
With this extension, you can register more storages with different roots, filesystem adapters etc. The first registered storage is also considered as the default storage.
Configuration example
extensions: 68publishers.file_storage: SixtyEightPublishers\FileStorage\Bridge\Nette\DI\FileStorageExtension 68publishers.file_storage: storages: default: config: base_path: /data/files filesystem: adapter: League\Flysystem\Local\LocalFilesystemAdapter(%wwwDir%/data/files) config: [] # an optional config for filesystem adapter assets: path/to/file.png: my/file.png # single file copying path/to/directory: my-directory # copy whole directory s3: config: host: https://my-bucket.s3.amazonaws.com filesystem: adapter: League\Flysystem\AwsS3V3\AwsS3V3Adapter(@s3client, my-bucket)
Storage config options
Basic usage
Generated DI Container will contain an autowired services of type FileStorageProviderInterface
and FileStorageInterface
(the default storage).
use Nette\DI\Container; use SixtyEightPublishers\FileStorage\FileStorageInterface; use SixtyEightPublishers\FileStorage\FileStorageProviderInterface; /** @var Container $container */ $defaultStorage = $container->getByType(FileStorageInterface::class); $provider = $container->getByType(FileStorageProviderInterface::class); $defaultStorage = $provider->get(); # or $defaultStorage = $provider->get('default'); $s3storage = $provider->get('s3');
Persisting files
use SixtyEightPublishers\FileStorage\FileStorageInterface; /** @var FileStorageInterface $storage */ # Create a resource from file or url: $resource = $storage->createResourceFromFile( $storage->createPathInfo('test/invoice.pdf'), __DIR__ . '/path/to/invoice.pdf' ); $storage->save($resource); # Create resource from file that is stored in storage: $resource = $storage->createResource( $storage->createPathInfo('test/invoice.pdf') ); # copy to the new location $storage->save($resource->withPathInfo( $storage->createPathInfo('test/invoice-2.pdf') ));
Check a file existence
use SixtyEightPublishers\FileStorage\FileStorageInterface; /** @var FileStorageInterface $storage */ if ($storage->exists($storage->createPathInfo('test/invoice.pdf'))) { echo 'file exists!'; }
Deleting files
use SixtyEightPublishers\FileStorage\FileStorageInterface; /** @var FileStorageInterface $storage */ $storage->delete($storage->createPathInfo('test/invoice.pdf'));
Create links to files
use SixtyEightPublishers\FileStorage\FileStorageInterface; /** @var FileStorageInterface $storage */ # /data/files/test/invoice.pdf echo $storage->link($storage->createPathInfo('test/invoice.pdf')); # or $fileInfo = $storage->createFileInfo($storage->createPathInfo('test/invoice.pdf')); echo $fileInfo->link();
Cleaning the storage
use Nette\DI\Container; use SixtyEightPublishers\FileStorage\FileStorageProviderInterface; use SixtyEightPublishers\FileStorage\Cleaner\StorageCleanerInterface; /** @var Container $container */ $cleaner = $container->getByType(StorageCleanerInterface::class); $provider = $container->getByType(FileStorageProviderInterface::class); $storage = $provider->get('default'); # get files count in the specific namespace: $cleaner->getCount($storage->getFilesystem(), [ StorageCleanerInterface::OPTION_NAMESPACE => 'test', ]); # get files count in the whole storage: $cleaner->getCount($storage->getFilesystem()); # remove files in the specific namespace: $cleaner->clean($storage->getFilesystem(), [ StorageCleanerInterface::OPTION_NAMESPACE => 'test', ]); # clean the whole storage: $cleaner->clean($storage->getFilesystem());
Assets copying
use Nette\DI\Container; use SixtyEightPublishers\FileStorage\FileStorageProviderInterface; use SixtyEightPublishers\FileStorage\Asset\AssetsCopierInterface; /** @var Container $container */ $copier = $container->getByType(AssetsCopierInterface::class); $provider = $container->getByType(FileStorageProviderInterface::class); # Copies assets defined in the configuration $copier->copy($provider->get('default')); $copier->copy($provider->get('s3'));
Assets can be defined in the configuration under each storage separately but compiler extensions can define other assets:
use Nette\DI\CompilerExtension; use SixtyEightPublishers\FileStorage\Bridge\Nette\DI\Assets; use SixtyEightPublishers\FileStorage\Bridge\Nette\DI\AssetsProviderInterface; final class MyCompilerExtension extends CompilerExtension implements AssetsProviderInterface { public function provideAssets() : array { return [ new Assets('s3', [ 'path/to/file1.jpeg' => 'namespace/file1.jpeg', 'path/to/file2.jpeg' => 'namespace/file2.jpeg', ]), ]; } }
Usage with Doctrine ORM
The package provides custom Doctrine DBAL type file_info
. You can register it manually in this way:
use Doctrine\DBAL\Types\Type; use SixtyEightPublishers\FileStorage\FileStorageProviderInterface; use SixtyEightPublishers\FileStorage\Bridge\Doctrine\DbalType\FileInfoType; /** @var FileStorageProviderInterface $fileStorageProvider */ Type::addType(FileInfoType::NAME, FileInfoType::class); # this line is important: Type::getType(FileInfoType::NAME)->setFileStorageProvider($fileStorageProvider);
Or you can use a compiler extension FileStorageDoctrineExtension
. The extension requires an integration of package 68publishers/doctrine-bridge.
extensions: 68publishers.file_storage.doctrine: SixtyEightPublishers\FileStorage\Bridge\Nette\DI\FileStorageDoctrineExtension 68publishers.file_storage.doctrine: type_name: file_info # default
Example entity and persistence
use Doctrine\ORM\Mapping as ORM; use SixtyEightPublishers\FileStorage\FileInfoInterface; /** * @ORM\Entity */ class File { # ID and other columns /** * @ORM\Column(type="file_info") */ private FileInfoInterface $source; public function __construct(FileInfoInterface $source) { $this->source = $source; } public function getSource(): FileInfoInterface { return $this->source; } }
use Doctrine\ORM\EntityManagerInterface; use SixtyEightPublishers\FileStorage\FileStorageInterface; /** @var EntityManagerInterface $em */ /** @var FileStorageInterface $storage */ $pathInfo = $storage->createPathInfo('test/avatar.png'); $resource = $storage->createResourceFromFile($pathInfo, __DIR__ . '/path/to/uploaded/file.png'); $storage->save($resource); $pathInfo = $pathInfo->withVersion(time()); $entity = new File($storage->createFileInfo($pathInfo)); $em->persist($entity); $em->flush(); # /data/files/test/avatar.png?_v=1611837352 echo (string) $entity->getSource();
Usage with Latte
extensions: 68publishers.file_storage.latte: SixtyEightPublishers\FileStorage\Bridge\Nette\DI\FileStorageLatteExtension
{varType SixtyEightPublishers\FileStorage\FileInfoInterface $fileInfo} {* method FileInfo::__toString() calls ::link() internally so both lines are the same: *} <a href="{$fileInfo->link()}" download>Download a file</a> <a href="{$fileInfo}" download>Download a file</a> {* Create FileInfo from string *} <a href="{file_info('test/invoice.pdf')}" download>Download a file</a>
Symfony Console commands
extensions: 68publishers.file_storage.console: SixtyEightPublishers\FileStorage\Bridge\Nette\DI\FileStorageConsoleExtension
Clean storage command:
$ bin/console file-storage:clean [<storage>] [--namespace <value>]
Copy storage assets:
$ bin/console file-storage:copy-assets [<storage>]
Contributing
Before opening a pull request, please check your changes using the following commands
$ make init # to pull and start all docker images
$ make cs.check
$ make stan
$ make tests.all