Здравствуй, %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
Можно использовать хэш содержимого, но тогда при хранении больших файлов вычисления хэша может быть медленным, хотя учитывая что большинство файлов маленькие, в этом есть смысл.