Skip to content

Commit 7957ad1

Browse files
committed
added Storage component
1 parent 3a54083 commit 7957ad1

File tree

12 files changed

+461
-2
lines changed

12 files changed

+461
-2
lines changed

app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,15 @@ private function syncImageToDatabase(string $fileName): void
180180
/**
181181
* @param string $fileName
182182
* @return array
183+
* @throws FileNotFoundException
183184
*/
184185
private function getFileMetadata(string $fileName): array
185186
{
186187
$metadata = [];
187188
try {
188-
$fileHandler = $this->mediaDirectory->stat($this->_mediaConfig->getMediaPath($fileName));
189-
$metadata['size'] = $fileHandler['size'];
189+
$info = $this->storageProvider->get('media')
190+
->getMetadata($this->_mediaConfig->getMediaPath($fileName));
191+
$metadata['size'] = $info['size'];
190192
} catch (FileSystemException $e) {
191193
$metadata['url'] = $this->getImageHelper()->getDefaultPlaceholderUrl('small_image');
192194
$metadata['size'] = 0;
@@ -199,6 +201,7 @@ private function getFileMetadata(string $fileName): array
199201
* Returns image json
200202
*
201203
* @return string
204+
* @throws FileNotFoundException
202205
*/
203206
public function getImagesJson()
204207
{
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Storage\AdapterFactory;
7+
8+
use League\Flysystem\AdapterInterface;
9+
10+
/**
11+
* Storage adapter factory
12+
*
13+
* A storage adapter should have a factory implementing this interface in order to be supported by Magento.
14+
* A new factory should be registered in \Magento\Framework\Storage\StorageProvider::$storageAdapters via di.xml.
15+
*/
16+
interface AdapterFactoryInterface
17+
{
18+
/**
19+
* Create instance of a storage adapter
20+
*
21+
* @param array $options
22+
* @return AdapterInterface
23+
*/
24+
public function create(array $options): AdapterInterface;
25+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Storage\AdapterFactory;
7+
8+
use Aws\S3\S3Client;
9+
use League\Flysystem\AdapterInterface;
10+
use League\Flysystem\AwsS3v3\AwsS3Adapter;
11+
use Magento\Framework\Storage\InvalidStorageConfigurationException;
12+
13+
/**
14+
* Factory for AWS S3 storage adapter
15+
*/
16+
class AwsS3Factory implements AdapterFactoryInterface
17+
{
18+
/**
19+
* @inheritdoc
20+
*/
21+
public function create(array $options): AdapterInterface
22+
{
23+
if (empty($options['client']) || empty($options['bucket'])) {
24+
throw new InvalidStorageConfigurationException(
25+
"Can't create AWS S3 adapter: required 'client' and/or 'bucket' options are absent"
26+
);
27+
}
28+
$client = new S3Client($options['client']);
29+
return new AwsS3Adapter($client, $options['bucket'], $options['prefix'] ?? '');
30+
}
31+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Storage\AdapterFactory;
7+
8+
use League\Flysystem\AdapterInterface;
9+
use League\Flysystem\AzureBlobStorage\AzureBlobStorageAdapter;
10+
use Magento\Framework\Storage\InvalidStorageConfigurationException;
11+
use MicrosoftAzure\Storage\Blob\BlobRestProxy;
12+
13+
/**
14+
* Factory for Azure storage adapter
15+
*/
16+
class AzureFactory implements AdapterFactoryInterface
17+
{
18+
/**
19+
* @inheritdoc
20+
*/
21+
public function create(array $options): AdapterInterface
22+
{
23+
if (empty($options['connection_string']) || empty($options['container_name'])) {
24+
throw new InvalidStorageConfigurationException(
25+
"Can't create Azure Blob storage adapter: " .
26+
"required 'connection_string' and/or 'container_name' options are absent"
27+
);
28+
}
29+
$client = BlobRestProxy::createBlobService($options['connection_string']);
30+
return new AzureBlobStorageAdapter($client, $options['container_name'], $options['prefix'] ?? null);
31+
}
32+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Storage\AdapterFactory;
7+
8+
use League\Flysystem\Adapter\Local;
9+
use League\Flysystem\AdapterInterface;
10+
use Magento\Framework\Storage\InvalidStorageConfigurationException;
11+
12+
/**
13+
* Factory for local filesystem storage adapter
14+
*/
15+
class LocalFactory implements AdapterFactoryInterface
16+
{
17+
public const ADAPTER_NAME = 'local';
18+
19+
/**
20+
* @inheritdoc
21+
*/
22+
public function create(array $options): AdapterInterface
23+
{
24+
if (empty($options['root'])) {
25+
throw new InvalidStorageConfigurationException(
26+
"Can't create local filesystem storage adapter: required 'root' option is absent"
27+
);
28+
}
29+
return new Local($options['root']);
30+
}
31+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Framework\Storage;
8+
9+
/**
10+
* Exception to be thrown in a case when storage is configured incorrectly
11+
*/
12+
class InvalidStorageConfigurationException extends \RuntimeException
13+
{
14+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
The Storage library provides abstraction over different file storage providers.
2+
3+
## Usage
4+
5+
A module that needs file storage, it can be configured via `\Magento\Framework\Storage\StorageProvider` in `di.xml`:
6+
7+
```xml
8+
<type name="Magento\Framework\Storage\StorageProvider">
9+
<arguments>
10+
<argument name="storage" xsi:type="array">
11+
<item name="storage-name" xsi:type="string">default/location</item>
12+
</argument>
13+
</arguments>
14+
</type>
15+
```
16+
17+
`default/location` is a default path in local filesystem, relative to Magento root.
18+
19+
Now, in a PHP class that uses the declared storage, use the same `\Magento\Framework\Storage\StorageProvider` to get it:
20+
21+
```php
22+
/**
23+
* @var \Magento\Framework\Storage\StorageProvider
24+
*/
25+
private $storageProvider;
26+
27+
public function doSomething()
28+
{
29+
$storage = $this->storageProvider->get('storage-name')
30+
$storage->put('path.txt', $content);
31+
}
32+
```
33+
34+
## Configuring Storage
35+
36+
A storage can be configured in `env.php`:
37+
38+
```php
39+
'storage' => [
40+
'storage-name' => [
41+
'adapter' => 'aws_s3',
42+
'options' => [
43+
'client' => [
44+
'credentials' => [
45+
'key' => '<key>',
46+
'secret' => '<secret>'
47+
],
48+
'region' => '<region>',
49+
'version' => 'latest',
50+
],
51+
'bucket' => '<bucket>',
52+
],
53+
],
54+
'media' => [
55+
// this is default configuration, so it doesn't need to be configured explicitly like so
56+
'adapter' => 'local',
57+
'options' => [
58+
'root' => 'pub/media'
59+
]
60+
]
61+
]
62+
```
63+
64+
Different providers have different `options` available for configuration.
65+
Under the hood, Magento Storage relies on [Flysystem](https://github.com/thephpleague/flysystem) library, so`options` might reflect options required by a corresponding storage adapter implemented for Flysystem.
66+
67+
## Storage Providers
68+
69+
By default, Magento Storage provides support for the following storage providers:
70+
71+
* Local filesystem (based on `\League\Flysystem\Adapter\Local`)
72+
* Adapter name: `local`
73+
* Options:
74+
```php
75+
[
76+
'root' => 'path/relative/to/magento/root'
77+
]
78+
```
79+
* AWS S3 V3 (based on `\League\Flysystem\AwsS3v3\AwsS3Adapter`)
80+
* Adapter name: `aws_s3`
81+
* Options:
82+
```php
83+
[
84+
'client' => [
85+
'credentials' => [
86+
'key' => '<key>',
87+
'secret' => '<secret>'
88+
],
89+
'region' => '<region>',
90+
'version' => 'latest',
91+
],
92+
'bucket' => '<bucket>',
93+
'prefix' => '<prefix>',
94+
]
95+
```
96+
* Azure Blob storage (based on `\League\Flysystem\AzureBlobStorage\AzureBlobStorageAdapter`)
97+
* Adapter name: `ms_azure`
98+
* Options:
99+
```php
100+
[
101+
'connection_string' => '<connection-string>',
102+
'container_name' => '<container-name>',
103+
'prefix' => '<prefix>',
104+
]
105+
```
106+
107+
Additional adapters can be added by:
108+
1. Creating an adapter factory implementing `\Magento\Framework\Storage\AdapterFactory\AdapterFactoryInterface`
109+
2. Registering the factory in `Magento\Framework\Storage\StorageProvider` via `di.xml`:
110+
```xml
111+
<type name="Magento\Framework\Storage\StorageProvider">
112+
<arguments>
113+
<argument name="storageAdapters" xsi:type="array">
114+
<item name="custom_adapter" xsi:type="string">My\Storage\AdapterFactory</item>
115+
</argument>
116+
</arguments>
117+
</type>
118+
```
119+
120+
The factory is registered as a "string" (name of the class).
121+
That's because in most cases only a few adapters will be really created for a single application, and we don't want to create unnecessary factory instances.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Storage;
7+
8+
use League\Flysystem\Filesystem;
9+
10+
/**
11+
* File storage abstraction
12+
*/
13+
class Storage extends Filesystem implements StorageInterface
14+
{
15+
16+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Storage;
7+
8+
use League\Flysystem\AdapterInterface;
9+
use Magento\Framework\ObjectManagerInterface;
10+
use Magento\Framework\Storage\AdapterFactory\AdapterFactoryInterface;
11+
12+
/**
13+
* Provider of storage adapters based on storage name
14+
*/
15+
class StorageAdapterProvider
16+
{
17+
/**
18+
* @var ObjectManagerInterface
19+
*/
20+
private $objectManager;
21+
22+
/**
23+
* @var array
24+
*/
25+
private $config;
26+
27+
/**
28+
* Constructor
29+
*
30+
* @param ObjectManagerInterface $objectManager
31+
* @param array $config
32+
*/
33+
public function __construct(ObjectManagerInterface $objectManager, array $config)
34+
{
35+
$this->objectManager = $objectManager;
36+
$this->config = $config;
37+
}
38+
39+
/**
40+
* Create storage adapter based on its name with provided options
41+
*
42+
* @param string $adapterName
43+
* @param array $options
44+
* @return AdapterInterface|null
45+
*/
46+
public function create(string $adapterName, array $options) :? AdapterInterface
47+
{
48+
if (!isset($this->config[$adapterName])) {
49+
throw new InvalidStorageConfigurationException(
50+
"Configured adapter '$adapterName' is not supported"
51+
);
52+
}
53+
$adapterFactoryClass = $this->config[$adapterName];
54+
$adapterFactory = $this->objectManager->get($adapterFactoryClass);
55+
if (!$adapterFactory instanceof AdapterFactoryInterface) {
56+
throw new InvalidStorageConfigurationException(
57+
"Configured storage adapter factory '$adapterFactory' must implement " .
58+
"'\Magento\Framework\Storage\AdapterFactory\AdapterFactoryInterface'"
59+
);
60+
}
61+
return $adapterFactory->create($options);
62+
}
63+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Storage;
7+
8+
use League\Flysystem\FilesystemInterface;
9+
10+
/**
11+
* Storage interface to be used by client code to manipulate objects in the storage
12+
*
13+
* Retrieve a real instance of storage via $storageProvider->get('<your-storage-name>'),
14+
* where $storageProvider is an instance of \Magento\Framework\Storage\StorageProvider
15+
*/
16+
interface StorageInterface extends FilesystemInterface
17+
{
18+
19+
}

0 commit comments

Comments
 (0)