Здравствуй, %habrauser%
doctrine c 2.5 версии поддерживает встраиваемые объекты, я написал библиотеку которая использует его как ссылка на файла.
Сегодня я расскажу как это облегчит управление загруженными файлами.
Поехали

Если вы не в курсе что такое встраиваемые объекты в doctrine, пожалуйста сначала прочитайте об этом
Установка
Как обычно, установим через 'composer'
composer require atom-azimov/uploader-bundle
и включаем в AppKernel.php
# app/AppKernel.php
public function registerBundles()
{
$bundles = [
...
new Atom\UploaderBundle\AtomUploaderBundle(),
...
];
}Отлично подходит для RAD приложений !

Допустим у нас есть сущность User и мы хотим дать пользователю возможность загружать свой аватар, c AtomUploaderBundle это реально легко:
# src/Entity/User.php
namespace Acme\Entity;
use Doctrine\ORM\Mapping\Embedded;
class User
{
...
/**
* @Embedded(class="Atom\Uploader\Model\Embeddable\FileReference")
*/
private $avatar;
}Это все что нужно!
Давайте разберёмся что же здесь происходит?
Мы встроили готовый встраиваемый объект в наш сущность, теперь AtomUploaderBundle при сохранении|обновлении сущноста проверяет есть ли там загруженные файлы, если есть генерирует название файла по умолчанию используя uniqid, и сохраняет файла в файловую систему по умолчанию в локальной машине в "%kernel.root_dir%/../web/uploads".
Наш встраиваемый объект 'Atom\Uploader\Model\Embeddable\FileReference' можно считать как ссылка на файла, он в конструкторе принимает экземпляр '\SplFileInfo' чего является файлы в Symfony, а также имеет методы для получения различную информацию о файле таких как относительный пут, публичный адрес или экземпляр '\SplFileInfo' последний отключено по умолчанию.
\SplFileInfoможет содержат любой ресурс который поддерживает функцияfopenт.е. можно кормит http ссылкой вместо файла.
Отлично настраивается !

Допустим мы хотим:
- Использовать свой объект вместо готового
FileReference. - Чтобы названия файла была хэшем оного.
- Использовать
flysystemкак абстракцию над файловой системой.
Наш встраиваемый объект будет выглядеть так:
# src/User/Avatar.php
namespace User;
use Doctrine\ORM\Mapping\Column;
class Avatar {
/**
* Только это поле должен хранится в БД
*
* @Column()
*/
private $file;
private $uri;
}Getter/Setter-ы не обязательны, в случае их отсутствия будут использоваться рефлексии, а ещё можно изменит стандартные название свойств или использовать методы.
Чтобы определит название файла нужен использовать готовый стратегия нейминга или создать новый,
из готовых пока есть unique_id и basename, а нам нужен хэш, так что наша стратегия нейминга будет выглядеть так:
# src/Naming/HashNamer.php
namespace Naming;
use Atom\Uploader\Naming;
class HashNamer implements INamer {
/**
* @param \SplFileInfo $file
*
* @return string
*/
public function name(\SplFileInfo $file): string {
// вычисляем и вернём хэш файла
}
}Его нужно зарегистрировать как сервис и назначить тег 'atom_uploader.namer_repo':
services:
hash_namer:
public: false
class: Namer\HashNamer
tags:
- { name: atom_uploader.namer_repo, strategy: hash }В теги добавили ключ strategy это название стратегии он будет использоваться при мэппинге.
Меппинг:
# app/config/config.yml
atom_uploader:
mappings:
User\Avatar:
...
naming_strategy: hash
fs_adapter: flysystem # Поддерживается из коробки, только не забывайте его установит :)
fs_prefix: my_fs_mountpoint # Точка монтирование в flysystem
...Это все, наш встраиваемый объект можно использовать как в первом примере, только теперь файлы хранятся в flysystem, и название файлов будут по другому.
Вот ссылка на github, там же есть более подробная документация на русском.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
to0n1
почему не исользовать хеш алгоритм как дефолтный нейминг? ведь всем известны проблемы с псевдо-рандомом!
cold147
Можно использовать хэш содержимого, но тогда при хранении больших файлов вычисления хэша может быть медленным, хотя учитывая что большинство файлов маленькие, в этом есть смысл.