bengor-file/doctrine-orm-bridge-bundle
## Getting Started
### Minimal Setup
1. **Install the Bundle**:
```bash
composer require bengor-file/doctrine-orm-bridge-bundle
Ensure FileBundle and DoctrineORMBridge are also installed (this bundle acts as a bridge between them).
Enable in config/bundles.php:
return [
// ...
BenGor\FileBundle\BenGorFileBundle::class => ['all' => true],
BenGor\DoctrineORMBridgeBundle\DoctrineORMBridgeBundle::class => ['all' => true],
];
First Use Case: Persisting a File Entity
Define an entity with a File property (e.g., User entity):
use BenGor\FileBundle\Model\File;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class User
{
#[ORM\OneToOne(targetEntity: File::class, cascade: ["persist"])]
private ?File $avatar = null;
// Getters/setters...
}
Use the File entity from FileBundle directly in Doctrine ORM entities.
Handle Upload in Controller:
use BenGor\FileBundle\Manager\FileManager;
use Symfony\Component\HttpFoundation\File\UploadedFile;
public function uploadAvatar(UploadedFile $file, FileManager $fileManager): Response
{
$user = $this->getUser();
$fileEntity = $fileManager->upload($file, 'avatars'); // 'avatars' is a configured storage path
$user->setAvatar($fileEntity);
$this->entityManager->persist($user);
$this->entityManager->flush();
return new Response('Avatar uploaded!');
}
Configure Storage in config/packages/ben_gor_file.yaml:
ben_gor_file:
storages:
avatars:
type: local
path: '%kernel.project_dir%/public/uploads/avatars'
allowed_mime_types: ['image/jpeg', 'image/png']
Accessing Files in Templates:
Use the file_url Twig filter (if configured):
<img src="{{ user.avatar|file_url }}" alt="Avatar">
Doctrine Lifecycle Callbacks:
Use @PrePersist/@PreUpdate to auto-generate filenames or validate files:
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class User
{
#[ORM\PrePersist]
public function prePersist(): void
{
if ($this->avatar) {
$this->avatar->setFilename(
uniqid() . '.' . $this->avatar->getMimeType()->getExtension()
);
}
}
}
Querying Files: Use DQL to filter entities by file properties:
$usersWithAvatars = $entityManager->createQuery(
'SELECT u FROM App\Entity\User u WHERE u.avatar IS NOT NULL'
)->getResult();
Bulk Operations: Leverage Doctrine’s batch processing for large file uploads:
$entityManager->getConnection()->beginTransaction();
try {
foreach ($uploadedFiles as $file) {
$fileEntity = $fileManager->upload($file, 'bulk_uploads');
$entity->setFile($fileEntity);
$entityManager->persist($entity);
if ($i % 20 === 0) {
$entityManager->flush();
$entityManager->clear();
}
}
$entityManager->flush();
$entityManager->getConnection()->commit();
} catch (\Exception $e) {
$entityManager->getConnection()->rollBack();
throw $e;
}
Outdated Dependencies:
symfony/polyfill) or fork the bundle for updates.Missing Doctrine Event Listeners:
PostRemove or use a custom event subscriber:
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\OnFlushEventArgs;
class FileCleanupSubscriber implements EventSubscriber
{
public function getSubscribedEvents(): array
{
return ['onFlush'];
}
public function onFlush(OnFlushEventArgs $args): void
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityDeletions() as $entity) {
if (method_exists($entity, 'getFile') && $entity->getFile()) {
$file = $entity->getFile();
if ($file->getPath()) {
unlink($file->getPath());
}
}
}
}
}
Register the subscriber in services.yaml:
services:
App\EventSubscriber\FileCleanupSubscriber:
tags: ['doctrine.event_subscriber']
File Permissions:
path in config) is writable by the web server user (e.g., www-data or nginx). Use chmod -R 775 cautiously or restrict permissions to the app’s storage directory.MIME Type Validation:
mime/mime-type-detector for MIME validation. If files are rejected unexpectedly, verify:
allowed_mime_types in config match the actual uploaded files.mime/mime-type-detector package is installed and up-to-date.Log File Operations:
Enable debug logging for ben_gor_file in config/packages/dev/monolog.yaml:
handlers:
file_log:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["file"]
Then configure the bundle to log:
ben_gor_file:
debug: true
Check File Entity State:
Use Doctrine’s getEntityManager()->getUnitOfWork()->getOriginalEntityData($entity) to compare pre- and post-persist states:
$originalData = $em->getUnitOfWork()->getOriginalEntityData($user);
dump($originalData['avatar']); // Compare with $user->getAvatar()
Storage Path Issues: If files aren’t saved, verify:
path in config is absolute and exists.touch %path%/test.txt).avatars vs avatar).Custom File Metadata:
Extend the File entity to add custom fields (e.g., altText, tags):
#[ORM\Entity]
class CustomFile extends File
{
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private ?string $altText = null;
// Getters/setters...
}
Update the bundle’s FileType to support the new fields in forms.
Custom Storage Backends:
Implement a new storage adapter by extending BenGor\FileBundle\Storage\StorageInterface:
class S3Storage implements StorageInterface
{
public function save(File $file, string $path): void
{
// Custom S3 logic
}
public function delete(string $path): void
{
// Custom S3 deletion
}
}
Register it in the bundle’s configuration:
ben_gor_file:
storages:
s3_uploads:
type: s3
adapter: App\Storage\S3Storage
bucket: my-bucket
Event Dispatching:
Listen for file events (e.g., file.pre_upload, file.post_save) using Symfony’s event dispatcher:
use BenGor\FileBundle\Event\FileEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class FileEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
FileEvents::PRE_UPLOAD => 'onPreUpload',
FileEvents::POST_SAVE => 'onPostSave',
];
}
public function onPreUpload
How can I help you explore Laravel packages today?