68publishers/file-storage

File management based on Flysystem with an integration into Nette Framework.

v1.1.3 2024-08-06 02:38 UTC

This package is auto-updated.

Last update: 2024-10-06 02:57:36 UTC


README

📁 File management based on Flysystem with an integration into Nette Framework.

Checks Coverage Status Total Downloads Latest Version PHP Version

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