How to make FileUpload Object handle Multiple files in Symfony2? - php

I'm trying to make a form with a multiple file field. Since the docs are quite vague:
http://symfony.com/doc/current/reference/forms/types/file.html
http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html
I've decided to use W3C FileReader API (Based in the doc urls below), to handle files from the client and manage the underliying data from the view to the entity. Currently supports drag&drop, metadata, and multiple selections on the client.
http://www.html5rocks.com/en/tutorials/file/dndfiles/
http://playground.html5rocks.com/#reading_file_metadata
https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
But I want to give the UploadedFile object one more chance, and the MAIN problem I have is that I can't make the file property in my entity (FileUpload type) to store more than one file data. My input looks like this:
<input type="file" id="upload_files" name="upload[files][]" required="required" multiple="multiple" />
In theory If I made the name to be an Array, the fileUpload should contain the files but it doesn't. Can UploadedFile object store multiple files data ? or just single ?
http://api.symfony.com/2.2/Symfony/Component/HttpFoundation/File/UploadedFile.html
Also tried to initialize (in the __construct of the entity) the $files property as an array and modify the setFiles() to store new array index's $this->files[] = $file; ... you know.
But then Symfony tells me that exception:
The form's view data is expected to be an instance of class Symfony\Component\HttpFoundation\File\File, but is a(n) array. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) array to an instance of Symfony\Component\HttpFoundation\File\File.
I'm not familiarized with data transformers. And Can't figure it out how can be done now. Or If it could be really useful to get the UploadedFile Object with every file data.
In synthesis... With this given info, and the code below. Could anyone help me to get the FileUpload object the correct number of files and not just the last added ? Thank You
Made a repo on github: https://github.com/jeflopo/fileupload
For brevity, here are the relevant files:
The form:
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Form/Type/FileUploadType.php
The Entity:
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Entity/FileUpload.php
The controller (Just see the uploadAction):
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Controller/DemoController.php
The View:
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Resources/views/Demo/upload.html.twig
The JavaScript that handles the files on the client (Doesn't affect to files behaviour in the server):
https://github.com/jeflopo/fileupload/blob/master/src/Acme/DemoBundle/Resources/public/js/upload.js

I created a pull request which fixes the error!
What's left to do now is that you create unique filenames for the uploaded files and then use the move method on each file. If you don't move them the files won't get saved!
As I set mapped to false, your entity doesn't contain the files. You have to create an array with the filenames you just created to save the file paths.

Related

Typo3 extension: Transfering an object via arguments doesn't work. What am i doing wrong?

Before i state my problem, please be aware that this is my first time working with Typo3 and/or creating an Extbase extension.
SO basically i want to create an extbase extension for Typo3, but i seem not to be able to wrap my head around the concept of transfering an object (assigned to the view of a specific template) via arguments to an action, with the purpose of attaching the object to another (with a 1:n relation).
My example:
I have an Objekt of the type "Appliance" assigned to the view of a template ("Show.html"). I can list all the properties of it in the Template, so it definitely exists in the view.
Now i want to create an Object of the type "Host" using a form and then attaching it to this specific "Appliance" object.
The problem is: I can't transfer the object of the type "Appliance" to the specific Action of the Controller of the type "Host" which itself should then assign it to the view of another template.
Look at the following code example:
<f:link.action action="new" controller="Host" arguments="{appliance:appliance}" >Add Host X</f:link.action>
This is the specific code line in the "Show.html" template that transfers the "Appliance" object to the Action "new" of the "Host" controller using arguments... The "Host" controller:
public function newAction(\Cjk\Icingaconfgen\Domain\Model\Appliance $appliance, \Cjk\Icingaconfgen\Domain\Model\Host $host = NULL)
{
$this->view->assign('appliance', $appliance);
$this->view->assign('host', $host);
}
At this point i get the following error message:
"Argument 1 passed to
Cjk\Icingaconfgen\Controller\HostController::newAction() must be an
instance of Cjk\Icingaconfgen\Domain\Model\Appliance, none given"
What am i doing wrong?
You need a Docblock that describes these parameters.
What may look like just comments, actually follows the PHPDoc standard. These declarations are interpreted by the TYPO3 ReflectionClass to map your Domain Model and validate parameters and object properties.
Make sure you completely flush the cache whenever you add or update one of these.
/*
* #param \Cjk\Icingaconfgen\Domain\Model\Appliance $appliance
* #param \Cjk\Icingaconfgen\Domain\Model\Host $host
* #return void
*
*/
public function newAction(\Cjk\Icingaconfgen\Domain\Model\Appliance $appliance, \Cjk\Icingaconfgen\Domain\Model\Host $host = NULL)
{
$this->view->assign('appliance', $appliance);
$this->view->assign('host', $host);
}
You need to be sure that there is an Appliance model given in your Fluid template, easily by debugging it before the link with e.g. <f:debug>{appliance}</f:debug>
If this is okay, you should add some doc comments above your newAction because Extbase is referring to that.
An example would be: (just as I am writing this, a good example was posted). :)

symfony form with new files via Vich Uploader

Iam trying to build a smarter upload structure for my app. until now I had the code for uploading files in the controller and everything was more manual. Now I want to use the VichUploader in Symfony, but I have problems to implement it with multiple files.
First, I dont have an Entity with a file but an Entity that holds multiple File Entities. To be more clearly: The Entity Document has a oneToMany relation to File. So I build a form with a CollectionType:
$builder->add('files', Type\CollectionType::class, [
'entry_type' => Type\FileType::class
])
But because there is not file yet when I add a new Document, no upload field is shown. And even when there are already files (on edit form), there shouldn't be upload fields shown but text fields with file names.
How can I achieve that? Do i still need to add an umapped field files_new() with a multiple FileType? Then the VichUploader automatic stuff would not work.
If you pass an empty file object you should have your html fil input as it is a collection
In your controller (I don't have your code)
$entity = new Entity()
$entity->addFile(new FileEntity())//Add an empty file object
$form = $this->createForm(YourType::class, $entity);
$form->handleRequest($request);

Laravel - Monitoring Record Change

I need an Eloquent model to represent a document uploaded to the server. One such field is the absolute path to the file. I want to be able to move the file whenever the database record is updated. I've seen that Laravel includes Observers to handle such events. The problem is:
Observers classes have method names which reflect the Eloquent events you wish to listen for. Each of these methods receives the model as their only argument.
This means that, unfortunately, with the given system I'm not able to actually move the file, since I will not have a way of retrieving both the current and new locations of the file in question. Is there another way of detecting when the value changes while having access to old and new values?
It sounds like you may be able to use the updating model event for this. You can register the event in the boot method of a service provider (such as app/Providers/AppServiceProvider.php) or by creating an observer class as you mentioned.
The isDirty and getOriginal methods should help you check if the file needs to be moved, and then get it's original path.
For example:
use Storage;
use App\Document;
public function boot()
{
Document::updating(function ($document) {
if ($document->isDirty('file_path')) {
// File needs to be moved
$current_path = $document->getOriginal('file_path');
$new_path = $document->file_path;
Storage::move($current_path, $new_path);
}
});
}
See also: Laravel updating eloquent event: getting the data

Create form for Uploadable Doctrine Extension

I'd like to use Uploadable to save some images (i.e. profile picture for users). I'm using many other Doctrine Extensions already (softdeletable, timestampable, blameable, etc.) so I thought it would be nice to use this one as well.
However, I don't know how to set up my Forms. The StofDoctrineExtensionsBundle documentation gives this example:
$document = new Document();
$form = $this->createFormBuilder($document)
->add('name')
->add('myFile')
->getForm()
;
//(...)
$uploadableManager->markEntityToUpload($document, $document->getMyFile());
In this example, is name the name of the Document or the name of the file?
Atlantic18/DoctrineExtensions's documentation adds a path, name, mimeType and size to an entity, to there is no myFile attribute.
Can anybody explain how to set up a Form for a Uploadable Doctrine entity? I couldn't find any documentation or good example that helped me further.
Entity
Like you've discovered, the documentation of DoctrineExtensions itself sheds some light on how to configure the Uploadable extension to use your entity.
Mainly by adding the #Gedmo\Uploadable annotation to your entity and #Gedmo\UploadableFilePath to the property that will contain the file path ($filePath for example).
Form
With the ->add() method of the form-builder you add fields to the form. The first parameter specifies the property-name of the corresponding entity. So ->add('myFile') would add a field for the property $myFile.
What you need to do is add a (file) field to the form for the property that will contain the file path ($filePath for example), and mark that property:
$form = $this->createFormBuilder($entity)
->add('filePath');
$uploadableManager->markEntityToUpload($entity, $entity->getFilePath());
In other words: myFile in your example should be replaced with filePath in my example, and whatever the actual property is in your real code.

SonataMediaBundle - How to use custom file input

I have a kind of scavenger hunt project in which I am using AngularJS to manage the different questions it may contain. These questions are of different types. Therefore, some may include a file input and some may not. In this project, I am also using Symfony and SonataMediaBundle to manage my files and my images.
Since my html model (mostly my forms) can change depending on the actions of the user, I cannot use Symfony's built-in tool to produce forms. Therefore, all my forms are custom made. This gives me a problem with SonataMediaBundle, when I want some files to be uploaded. If a user selects a file, this file will be sent via POST to a method in the controller, when the form gets sent. Therefore, I want to send this received file to SonataMediaBundle so that it can manage it, but I haven't found anywhere in the documentation how to do such a thing.
Theoretically, it is really simple. In my controller, when I get a file input, I want to let SonataMedia manage the upload (that is the copy to the proper location, etc...) and I have no clue on how I should do that.
Using symfony2 and not utilizing its benefits you are doing a big mistake you should built your app properly but as far as concerned to your question nothing is bounded by symfony but its on your own how you use it.You can get the sonata media manager service from the container and you have to manually set the all the required setters for the media manager and you have to manually work for the validations like file size ,file mimetype etc. Below is the demo how you can store the file in the sonata media bundle
/* Before class use these*/
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Application\Sonata\MediaBundle\Entity\Media;
public function uploadAction()
{
$file = $this->get('request')->files->get('filefieldname');
if (!$file instanceof UploadedFile || !$file->isValid()) {
return new Response(json_encode(array(
'event' => 'uploader:error',
'data' => array(
'message' => 'Missing file.',
),
)));
}
/* validate max min size for the file */
/* validate mime type for the file */
/* Get sonata media manager service from container */
$mediaManager = $this->container->get('sonata.media.manager.media');
/* create new instance of sonata media class in my case entity is located at
* Application\Sonata\MediaBundle\Entity\Media
*/
$media = new Media();
$media->setBinaryContent($file);
$media->setContext('default');
$ImagemimeTypes = array('image/jpeg', 'image/png');
$FilemimeTypes = array('application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/msword', 'application/pdf', 'application/x-pdf');
if (in_array($file->getMimeType(), $FilemimeTypes)) {
$media->setProviderName('sonata.media.provider.file');
}
if (in_array($file->getMimeType(), $ImagemimeTypes)) {
$media->setProviderName('sonata.media.provider.image');
}
/*If you have other providers in your app like vimeo /dailymotion etc
then do set here i have shown you the demo for adding image/file in sonata media */
/*other setters if you want to set like enabled/disable etc*/
$mediaManager->save($media);
// end function
}
But once again there will be alot of rework you have to do which symfony already provides you the ease for
The Bundle is there to close exactly this gap between Symfony and SonataMedia. SonataMedia is made for raw PHP, while the SonataMediaBundle attaches Symfony interfaces to SonataMedia; you're rewriting large part of the Bundle's functionality. To get a good example of how it is done right, look at the Bundle's code ;)
Sorry if the next suggestion is something you considered thoroughly, I just cannot be certain based on the limited information in the question. Symfony forms are highly flexible and provide a lot of critical functionality that you should not try to re-implement yourself (like CSRF-tokens). Most problems have a good solution with the form system. If a form is metamorphic (has many constellation of fields, based on UI interaction) and cannot be handled by multiple form types, you still have options. If you can set a GET parameter indicating which type of form is currently being sent then you can pass that to the FormBuilder. You may even choose to build a single big form for the whole application, which contain every field you ever use - that's a bit of a waste but still better than trying to wire posting+sonata together. The most hurt the first version does are some extra bytes and empty field. The least hurt you try could do is introduce serious security leaks.

Categories