betamfd / file-handler-bundle
Handle files from symfony
Requires
- php: ^7.2 || ^8.1
- doctrine/orm: ^2.4.8 || ^3.0
- symfony/filesystem: ^3.4 || ^4.0 || ^5.0
- symfony/http-foundation: ^3.4 || ^4.0 || ^5.0
- symfony/monolog-bundle: ^2.11 || ^3.1
- symfony/security: ^3.4 || ^4.4 || ^5.0
- symfony/yaml: ^3.0 || ^4.0 || ^5.0
README
This is a simple file handler I've built to use across several of my Symfony projects. It takes a handled form and creates a File entity, filling it, and saves your file to the drive in a customizable location.
This was primarily written for private uploads - images and documents that need to be behind a login. As of version 2.0.0, it now allows for a public path to be set so that uploads can be set to a public location like /web/
or /public/
depending on your setup. The change does require a database update and a change to the configuration variables. File uploads will default to the private location.
Composer Install
composer require betamfd/file-handler-bundle
Enable the Bundle
Enable the bundle by adding it to the list of registered bundles
in the app/AppKernel.php
file of your project:
new BetaMFD\FileHandlerBundle\BetaMFDFileHandlerBundle(),
Or if you're using a newer version of Symfony, enable it in the config/bundles.php
file of your project:
BetaMFD\FileHandlerBundle\BetaMFDFileHandlerBundle::class => ['all' => true],
Add Entities
Create a File Entity and extend it from the model. Create an ID field. It doesn't have to be a generated value but if it's not, you may want to write your own setter with any custom logic. A basic setter is in the model.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="file")
* @ORM\Entity(repositoryClass="FileRepository")
*/
class File extends \BetaMFD\FileHandlerBundle\Model\File
{
/**
* @var integer
*
* @ORM\Id
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
}
//any other fields or logic you want
}
<?php
namespace App\Entity;
class FileRepository extends \BetaMFD\FileHandlerBundle\Model\FileRepository
{
// any custom logic you want
}
You can use whatever you want for a user entity. At this time the only requirement is that there is a function getName() which returns a string, preferably the user's name for logging purposes.
Implement the user interface to ensure compatibility with required functions.
<?php
// src/App/Entity/User.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="user")
*/
class User
implements \BetaMFD\FileHandlerBundle\Model\UserInterface
{
// ...
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
// ...
}
You must tell the code where the User entity is
# app/config/config.yml
doctrine:
orm:
resolve_target_entities:
# Set this to your User Entity
BetaMFD\FileHandlerBundle\Model\UserInterface: App\Entity\User
Custom Settings
There are a few settings you should review and update. Depending on your version of symfony this could be in config.yml
or config/packages/beta_mfd_file_handler.yaml
beta_mfd_file_handler:
private_upload_location: '%kernel.project_dir%/var/uploads/'
public_upload_location: '%kernel.project_dir%/web/uploads/'
file_entity: 'App\Entity\File'
Using the File Handler Service
In your controller, inject \BetaMFD\FileHandlerBundle\Service\FileHandler
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as Controller;
class SomeController extends Controller
{
private $fileService;
public function __construct(
\BetaMFD\FileHandlerBundle\Service\FileHandler $fileService
) {
$this->fileService = $fileService;
}
public function useFormAction(Request $request)
{
//Make sure to use your own upload file form!
$form = $this->createForm(UploadFileType::class);
$form->handleRequest($request);
if ($form->isSubmitted() and $form->isValid()) {
$service = $this->fileService;
$service->allowAll(); //allow all allowed filetypes
//set the file, check the filename and extension
$service->handleForm($form);
//OPTIONAL: rename file
$new_name = 'YourPrefix_' . $service->getUnspacedFileName();
//use isExtensionOkay() or testFileType()
//The file type was tested in handleForm() and isExtensionOkay()
// is just returning the boolean flag set in handleForm()
if ($service->isExtensionOkay() and $service->testName($new_name)) {
//file a-okay!
$file = $service->newFile('rga', $new_name);
if ($file) {
// your custom logic
// like attach file entity to another entity or whatever here
$em = $this->getDoctrine()->getManager();
$em->flush();
//return whatever you need here
$this->addFlash('success', 'File added!');
return $this->redirectToRoute('some_route');
} else {
foreach ($service->getErrors() as $e) {
$this->addFlash('error', $e);
}
return $this->redirectToRoute('some_route');
}
} else {
//there were errors
//add flash errors and stop trying to upload.
foreach ($service->getErrors() as $e) {
$this->addFlash('error', $e);
}
//return something here or don't and just load the page
}
}
$return['files_form'] = $form->createView();
return $this->render('your_awesome_form_template.html.twig', $return);
}
public function straightFileHandlingAction(Request $request)
{
$file = $request->files->get('file');
$service = $this->fileService;
$service->allowAll(); //allow all allowed filetypes
$service->setUploadedFile($file);
//OPTIONAL: rename file
$new_name = 'YourPrefix_' . $service->getUnspacedFileName();
if ($service->testFileType() and $service->testName($new_name)) {
//file a-okay!
$file = $service->newFile('your_subfolder', $new_name);
if ($file) {
// your custom logic
// like attach file entity to another entity or whatever here
$em = $this->getDoctrine()->getManager();
$em->flush();
//return whatever you need here
return new Response();
} else {
$err = $service->getErrors();
//exception or flash bag
throw new \Exception(reset($err));
}
} else {
//there were errors
//exception or flash bag
$err = $service->getErrors();
throw new \Exception(reset($err));
}
//return whatever you need to here
}
}