pentatrion/upload-bundle

Upload Helper and File Manager for your Symfony App

Maintainers

👁 lhapaipai

Package info

github.com/lhapaipai/upload-bundle

Type:symfony-bundle

pkg:composer/pentatrion/upload-bundle

Statistics

Installs: 4 569

Dependents: 0

Suggesters: 0

Stars: 4

Open Issues: 2

v1.0.1 2023-02-24 16:21 UTC

Requires

Requires (Dev)

Suggests

Provides

None

Conflicts

None

Replaces

None

MIT 946d81a0a8769af88f7d2ea7b2ad4a790ef18a8f

  • Hugues Tavernier <hugues.tavernier.woop@protonmail.com>

symfonybundleuploadfile-manager


README

👁 Symfony logo

UploadBundle for your Symfony application.

This Symfony bundle provides :

  • Upload Helpers
  • Endpoints for a Mini File Manager in your Symfony Application.
  • Twig functions for your uploaded files
  • FilePickerType for your forms

Installation

composer require pentatrion/upload-bundle

Recommended optional dependencies

  • symfony/validator : required for upload file validation
  • liip/imagine-bundle : required to use thumbnails with your file manager.
  • imagine/imagine : required for image modification (resize, crop, rotation)
composer require symfony/validator liip/imagine-bundle imagine/imagine

Other dependencies

  • symfony/security-bundle : only required with FilePickerType

If you have installed liip/imagine-bundle, configure at least the small filter for your thumbnails.

# config/packages/pentatrion_upload.yaml
# default configuration
pentatrion_upload:
 liip_filters: ["small"]
# config/packages/liip_imagine.yaml
liip_imagine:
 driver: "gd"

 # define filters defined in pentatrion_upload.liip_filters
 filter_sets:
 small:
 filters:
 thumbnail: { size: [250, 250], mode: inset, allow_upscale: true }

Create directories with Apache user access in upload path and liipImagineBundle cache path (public/uploads, public/media)

mkdir public/{uploads,media}
chmod 777 public/{uploads,media}

Utilisation

FileHelper

in your FormType create a non mapped FileType

use App\Entity\Post;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;


class PostType extends AbstractType
{
 public function buildForm(FormBuilderInterface $builder, array $options)
 {
 $builder
 // ...
 ->add('image', FileType::class, [
 'mapped' => false,
 'required' => false
 ])
 ;
 }
 // ...
}

in your Controller

class PostController extends AbstractController
{
 /**
 * @Route("/{id}/edit", name="post_edit", methods={"GET","POST"})
 */
 public function edit(Request $request, Post $post, FileHelper $fileHelper): Response
 {
 $form = $this->createForm(PostType::class, $post);
 $form->handleRequest($request);

 if ($form->isSubmitted() && $form->isValid()) {
 $image = $form['image']->getData();
 if ($image) {
 /** @var UploadedFile $uploadedFile */
 $uploadedFile = $fileHelper->uploadFile($image, 'posts');

 // posts/my-image.jpg
 $post->setImagePath($fileInfos->getUploadRelativePath());
 }

 $this->getDoctrine()->getManager()->flush();
 // ...
 }
 // ...
 }
}
$file // file from $_FILES
$directory = 'relative/path'; // path to add into public_uploads.

// configured in config/packages/pentatrion_upload.yaml
// pentatrion_upload.origins.<origin>
// default value public_uplads -> "%kernel.project_dir%/public/uploads"
$originName = 'public_uploads';

$options = [
 'forceFilename' => 'beach.jpg',
 'prefix' => null, // prefix your file name 'img-'
 'guessExtension' => false, // guess Extension from content
 //(if you don't trust the uploader)
 'urlize' => true, // my File Name.jPg -> my-file-name.jpg
 'unique' => true, // suffixe your file with hash
 // beach-[hash].jpg
];

$fileInfos = $fileHelper->uploadFile($file, $directory, $originName, $options);

print_r($fileInfos);
{
 "id": null,
 "liipId": "@public_uploads:beach.jpg",
 "mimeGroup": "image",
 "mimeType": "image/jpeg",
 "filename": "beach.jpg",
 "directory": "",
 "origin": "public_uploads",
 "imageWidth": 1200,
 "imageHeight": 1200,
 "type": "file",
 "size": 134558,
 "updatedAt": "2021-08-07T23:12:09+02:00",
 "icon": "image-jpg.svg",
 "public": false,
 "absolutePath": null,
 "uploadRelativePath": "beach.jpg"
}

Twig functions

The bundle provide 2 twig functions :

  • uploaded_file_web_path('path/to/file', '')
  • uploaded_image_filtered('path/to/file', 'filter', '')
class PageController extends AbstractController
{
 #[Route('/page/{slug}', name: 'page_show')]
 public function show(Page $page): Response
 {
 return $this->render('page/show.html.twig', [
 'page' => $page
 ]);
 }

in your page/show.html.twig template

<!-- folder/my-uploaded-file.pdf -->
{{ page.imagePath }}

<!-- /uploads/folder/my-uploaded-file.pdf -->
{{ uploaded_file_web_path(page.imagePath) }}

<!-- /media-manager/get/show/private_uploads/folder/my-uploaded-file.pdf -->
{{ uploaded_file_web_path(page.imagePath, "private_uploads") }}

<!-- for your original -->
<!-- <img src="/uploads/folder/logo.jpg"/> -->
<img src="{{ uploaded_file_web_path(page.imagePath) }}"/>

<!-- for your cropped image (250x250px) -->
<!-- <img src="http://localhost/media/cache/resolve/small/posts/logo.jpg"/> -->
<img src="{{ uploaded_image_filtered(page.imagePath, 'small') }}"/>

<!-- <img src="http://localhost/media/cache/resolve/small/posts/logo.jpg"/> -->
<img src="{{ uploaded_image_filtered(page.imagePath, 'small', 'private_uploads') }}"/>

with Mini File Manager JS library.

this bundle has been designed to integrate perfectly with Mini File Manager.

He offers /media-manager endpoint for the backend integration.

npm i mini-file-manager

without helpers

<head>
 <link rel="stylesheet" href="/dist/style.css" />
</head>
<body>
 <div id="file-manager"></div>

 <script type="module">
 import { fileManager } from "mini-file-manager";
 let config = {
 "endPoint": "/media-manager",
 "entryPoints": [
 {
 "directory": "",
 "origin": "public_uploads",
 "readOnly": false,
 "icon": "fa-lock",
 "label": "Uploads",
 "webPrefix": "/uploads"
 }
 ]
 };

 fileManager("#file-manager", config);
 </script>
</body>

with helpers

use Pentatrion\UploadBundle\Service\FileManagerHelper;

class ManagerController extends AbstractController
{
 #[Route('/manager', name: 'manager')]
 public function index(FileManagerHelperInterface $fileManagerHelper): Response
 {
 $config = $fileManagerHelper->completeConfig([
 'entryPoints' => [
 [
 'label' => 'Uploads',
 'directory' => '',
 'origin' => 'public_uploads',
 'readOnly' => false,
 'icon' => 'fa-lock'
 ]
 ],
 'fileUpload' => [
 'maxFileSize' => 10 * 1024 * 1024,
 ],
 ]);
 return $this->render('manager/index.html.twig', [
 'fileManagerConfig' => $config,
 ]);
 }
}

Twig template for the file manager. the mini-file-manager config is placed in data-props attribute.

<head>
 <link rel="stylesheet" href="/dist/style.css" />
</head>
<body>
 <div id="file-manager" data-minifilemanager="{{ fileManagerConfig | json_encode | e('html_attr') }}"></div>

 <script type="module">
 import { fileManager } from "mini-file-manager";
 fileManager("#file-manager");
 </script>
</body>

Twig template for the file picker.

<head>
 <link rel="stylesheet" href="/dist/style.css" />
</head>
<body>
 <button id="find-file" data-minifilemanager="{{ fileManagerConfig | json_encode | e('html_attr') }}">Find</button>

 <script type="module">
 import { fileManagerModal } from "mini-file-manager";
 let pickerElt = document.querySelector("#file-picker");
 let options = JSON.parse(pickerElt.dataset.minifilemanager);

 pickerElt.addEventListener("click", () => {
 fileManagerModal(options, (selectedFiles) => {
 console.log(selectedFiles);
 });

 })
 </script>
</body>

If you want more details about configuration, check Mini File Manager.

If you want exemple, check Mini File Manager Template

FilePickerType with mini-file-manager for your form

use Pentatrion\UploadBundle\Form\TextFilePickerType;

class AdminUserFormType extends AbstractType
{
 public function buildForm(FormBuilderInterface $builder, array $options)
 {
 $builder
 ->add('poster', TextFilePickerType::class, [
 'required' => false,
 'fileManagerConfig' => [
 'entryPoints' => [
 [
 'label' => 'Uploads',
 'directory' => 'form',
 'origin' => 'public_uploads',
 ],
 ],
 'fileUpload' => [
 'maxFileSize' => 512 * 1024 * 1024,
 ],
 'fileValidation' => [
 'imageOptions' => [
 'ratio' => 1,
 ],
 ],
 'multiple' => false
 ],
 ])
 ;
 }
}

for full-list of imageOptions, have a look into mini-file-manager#Configuration repository.

add custom form theme for your form builder :

# config/packages/twig.yaml
twig:
 default_path: "%kernel.project_dir%/templates"
 form_themes: ["_form_theme.html.twig"]

look at _form_theme.html.twig

{# templates/_form_theme.html.twig #}
{%- block file_picker_widget -%}
 {%- set attr = attr|merge({
 class: (attr.class|default('') ~ ' file-picker')|trim,
 'data-files': files,
 'data-minifilemanager': filemanager_config,
 'data-formpreview': formpreview_config
 }) -%}
 {{- block('form_widget_simple') -}}
{%- endblock -%}

Advanced Bundle Configuration

configure your upload directories

package configuration

# config/packages/pentatrion_upload.yaml
pentatrion_upload:
 # Advanced config
 # must implement UploadedFileHelperInterface
 uploaded_file_helper: 'App\Service\AppUploadedFileHelper'

 origins:
 # choose the name of your choice
 public_uploads:
 # if directory is inside %kernel.project_dir%/public, files
 # will be directly accessible.
 path: "%kernel.project_dir%/public/uploads"
 # prefix to add in order to be found by a liip_imagine loader
 liip_path: "/uploads"
 private_uploads:
 path: "%kernel.project_dir%/var/uploads"
 liip_path: ""

 # if multiple origins
 default_origin: "public_uploads"

 # when you get infos from uploaded file put filters (liip_imagine.filter_sets)
 # you want the url used with mini-file-manager, put "small" to get thumbnails
 liip_filters: ["small", "large"]

if you set your class who implement UploadedFileHelperInterface (file_infos_helper option), you can extends UploadedFileHelper base class.

<?php
namespace App\Service;
use Pentatrion\UploadBundle\Service\UploadedFileHelper;

class AppUploadedFileHelper extends UploadedFileHelper
{
}

You have to add 3 binding for your constructor

#config/services.yaml
services:
 # default configuration for services in *this* file
 _defaults:
 autowire: true # Automatically injects dependencies in your services.
 autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
 bind:
 $liipFilters: "%pentatrion_upload.liip_filters%"
 $defaultOriginName: "%pentatrion_upload.default_origin%"
 $uploadOrigins: "%pentatrion_upload.origins%"

Manual installation

composer require pentatrion/vite-bundle

if you do not want to use the recipe or want to see in depth what is modified by it.

add upload routes to your Symfony app.

# config/routes/pentatrion_upload.yaml

# routes starting with /media-manager
_pentatrion_upload:
 prefix: /media-manager
 resource: "@PentatrionUploadBundle/Resources/config/routing.yaml"

add a config file for your bundle

# config/packages/pentatrion_upload.yaml
# default configuration
pentatrion_upload:
 uploaded_file_helper: 'Pentatrion\UploadBundle\Service\UploadedFileHelper'
 origins:
 public_uploads:
 path: "%kernel.project_dir%/public/uploads"
 liip_path: "/uploads"

 # update to ["small"] if you have installed liip/imagine-bundle
 liip_filters: []

if you have installed liip/imagine-bundle add the small filter.

# config/packages/liip_imagine.yaml
liip_imagine:
 driver: "gd"

 # define filters defined in pentatrion_upload.liip_filters
 # (at least small filter)
 filter_sets:
 small:
 filters:
 thumbnail: { size: [250, 250], mode: inset, allow_upscale: true }

 loaders:
 default:
 filesystem:
 data_root:
 # must be linked with pentatrion_upload -> origin.[origin-name].liip_path
 - "%kernel.project_dir%/public"