Filesystem
The filesystem abstraction is built on Flysystem and consists of three layers: adapter factories (create Flysystem adapters for a named backend), a filesystem factory (wraps adapters in typed reader/writer instances), and a pool (the primary injection point with pre-wired convenience methods for each well-known storage location).
Using the pool
Inject FilesystemPoolInterface and use the convenience methods for the location you need. The
pool caches instances internally, so multiple calls with the same arguments return the same object.
use Amarant\Framework\Contract\Factory\Filesystem\FilesystemPoolInterface;
public function __construct(
private readonly FilesystemPoolInterface $filesystemPool
) {
}
public function writeReport(string $content): void
{
$writer = $this->filesystemPool->getApplicationWriter('var/reports');
$writer->write('report.csv', $content);
}
public function readConfig(string $path): string
{
$reader = $this->filesystemPool->getApplicationReader();
return $reader->read($path);
}
Convenience methods
| Method | Adapter | Root |
|---|---|---|
getLocalWriter(?string $location) |
local |
$location (default /) |
getLocalReader(?string $location) |
local |
$location (default /) |
getApplicationWriter(?string $subdirectory) |
application |
{app.root}[/subdirectory] |
getApplicationReader(?string $subdirectory) |
application |
{app.root}[/subdirectory] |
getMediaWriter(?string $subdirectory) |
configured media adapter | subdirectory within the adapter's root |
getPrivateWriter(?string $subdirectory) |
configured private adapter | subdirectory within the adapter's root |
getTempWriter(?string $subdirectory) |
configured temp adapter | subdirectory within the adapter's root |
getLogWriter(?string $subdirectory) |
configured log adapter | subdirectory within the adapter's root |
getWriter(string $name, ?string $location) |
any adapter by name | $location passed to adapter |
getReader(string $name, ?string $location) |
any adapter by name | $location passed to adapter |
Methods returning a "writer" return FilesystemOperatorInterface (read + write). Methods returning
a "reader" return FileSystemReaderInterface (read-only).
The adapter used for getMediaWriter, getPrivateWriter, getTempWriter, and getLogWriter is
determined at runtime from configuration (see Storage configuration).
Operator interfaces
| Interface | Extends | Description |
|---|---|---|
FileSystemReaderInterface |
Flysystem FilesystemReader |
Read operations + getPublicUrl(string): ?string + checksum(string): string |
FileSystemWriterInterface |
Flysystem FilesystemOperator |
Full Flysystem operator (read + write + move + delete + list) |
FilesystemOperatorInterface |
FileSystemReaderInterface + FileSystemWriterInterface |
Combines both — returned by all writer methods |
All standard Flysystem methods are available: read(), readStream(), write(), writeStream(),
delete(), deleteDirectory(), createDirectory(), move(), copy(), fileExists(),
directoryExists(), listContents(), mimeType(), fileSize(), lastModified(), etc.
getPublicUrl() returns a public-facing URL for the file when the adapter supports it (e.g.
merchant-local and S3 adapters), or null for adapters without URL generation.
Built-in adapters
| Name | Class | Root | Public URL |
|---|---|---|---|
local |
LocalFilesystemAdapterFactory |
$location argument (default /) |
No |
application |
ApplicationFilesystemAdapterFactory |
{app.root} |
No |
application-log |
ApplicationLogFilesystemAdapterFactory |
{app.root}/var/log |
No |
application-private |
ApplicationPrivateFilesystemAdapterFactory |
{app.root}/private |
No |
application-temp |
ApplicationTempFilesystemAdapterFactory |
{app.root}/var |
No |
merchant-local |
MerchantLocalFilesystemAdapterFactory |
Merchant shared filesystem path | Yes — merchant base media URL |
s3_public |
S3FilesystemAdapterFactory |
S3 bucket root (public ACL) | Yes — S3 public URL |
s3_private |
S3PrivateFilesystemAdapterFactory |
S3 bucket root (private ACL) | No |
local is a bare adapter that takes any absolute $location. application is identical but
always roots at the application root, resolved from the kernel context. The application-*
variants are convenience aliases into well-known subdirectories.
The merchant-local adapter wraps LocalFilesystemAdapter with a PublicUrlGenerator decorator
that prepends the merchant's base media URL to any path, making getPublicUrl() work correctly
in multi-tenant environments.
Storage configuration
The adapter used for each logical storage type is set in the store configuration. The value must
be the adapter name string (the value returned by FilesystemAdapterFactoryInterface::getName()).
| Config key | FilesystemConfiguration method |
Default intention |
|---|---|---|
system/filesystem/media_type |
getMediaFilesystemType() |
Public media files (images, documents) |
system/filesystem/private_type |
getPrivateFilesystemType() |
Private files (invoices, exports) |
system/filesystem/temp_type |
getTempFilesystemType() |
Temporary files, uploads in progress |
system/filesystem/log_type |
getLogFilesystemType() |
Application logs |
S3/compatible storage settings (each has separate public and private variants):
| Config key | Description |
|---|---|
system/filesystem/type/s3/region |
AWS region |
system/filesystem/type/s3/bucket_name |
Bucket name |
system/filesystem/type/s3/endpoint |
Custom endpoint URL (for S3-compatible services) |
system/filesystem/type/s3/access_key |
Access key ID |
system/filesystem/type/s3/secret_key |
Secret access key |
system/filesystem/type/s3_private/region |
(same fields, private variant) |
Adding a custom adapter
Implement FilesystemAdapterFactoryInterface. Implement
SelectableFilesystemAdapterFactoryInterface instead if the adapter should appear in the backoffice
storage type dropdown.
use Amarant\Framework\Contract\Factory\Filesystem\SelectableFilesystemAdapterFactoryInterface;
use Amarant\Framework\Contract\Io\FilesystemAdapterInterface;
use League\Flysystem\FilesystemAdapter;
use Override;
final class GcsFilesystemAdapterFactory implements SelectableFilesystemAdapterFactoryInterface
{
public function __construct(
private readonly GcsConfiguration $config
) {
}
#[Override] public static function getName(): string
{
return 'gcs_public';
}
#[Override] public static function getLabel(): string
{
return 'Google Cloud Storage (public)';
}
#[Override] public function create(?string $location = null): FilesystemAdapter|FilesystemAdapterInterface
{
// build and return a Flysystem FilesystemAdapter
}
}
Then extend the FilesystemFactoryInterface binding to add your adapter to the adapterFactories
map:
use Amarant\Framework\Contract\Di\ConfigurableContainerInterface;
use Amarant\Framework\Contract\Factory\Filesystem\FilesystemFactoryInterface;
use Amarant\Framework\Enum\DiEnum;
use Vendor\ModuleName\Factory\Filesystem\Adapter\GcsFilesystemAdapterFactory;
$container->extend(
FilesystemFactoryInterface::class,
function (\Amarant\Framework\Contract\Di\BindInterface $bind, ConfigurableContainerInterface $container) {
$bind->addValueToArgument(
'adapterFactories',
DiEnum::getIdInjection(GcsFilesystemAdapterFactory::class),
GcsFilesystemAdapterFactory::getName()
);
}
);
After registration, use the adapter by name via getWriter('gcs_public') / getReader('gcs_public'),
or set one of the configuration keys (e.g. system/filesystem/media_type) to 'gcs_public' to
route a logical storage slot to it.
FinderFactory
For searching local files by name, extension, or content, inject FinderFactory and call
create() to get a fresh Symfony Finder instance:
use Amarant\Framework\Factory\Filesystem\FinderFactory;
public function __construct(
private readonly FinderFactory $finderFactory
) {
}
public function findPhpFiles(string $directory): iterable
{
return $this->finderFactory->create()
->in($directory)
->name('*.php')
->files();
}
Each call to create() returns a new, unconfigured Finder. FinderFactory does not use
Flysystem — it operates directly on the local filesystem via Symfony Finder.