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.
Related
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);
In my CakePHP 3 app, I have a somewhat elaborate tree of entities that I need to clone and save.
The root of the structure is a Questionnaire, a Questionnaire hasMany Questions, each Question hasMany Fields, etc. (it goes deeper). Now I want the user to be able to define a new questionnaire by copying an old one. Then they can change whatever they need.
I can get a dump of what I need to copy by using $questionnaire->$this->Questionnaires->get($id) with the appropriate contain fields. Is there a clever way to save this as a bunch of new entities while preserving the data and the structure between them?
I think the best possible way would be following work flow:
Get object you want to clone
Go through the collection and remove all ID's
Convert to array and use that in $this->Questionnaires->newEntity($arrayData, ['associated' => ['Questions', '...']]);
Now save the new entity with all the related data you want to keep
AFAIK there's no "smarter" way of cloning an entity with associations in Cake 3 :-)
You could also use this plugin. You only have to configure the behavior and it gives you other functionalities, like setting default values or appending text to some field.
use EntityInteface toArray() to get all its fields:
$newEtity = $someTable->newEntity($oldEtity->toArray());
unset($newDevice->created);
unset($newDevice->id);
$someTable->save($newEntity);
$original = $this->Table->get($id)->toArray();
$copy = $this->Table->newEntity();
$copy = $this->Table->patchEntity($copy, $original);
unset($copy['id']);
// Unset or modify all others what you need
$this->Table->save($copy);
Work perfectly like this :)
Here's the way I did it when I needed something similar (adapted to the Questionnaire example):
// load the models
$this->loadModel('Questionnaire');
$this->loadModel('Questions');
// get the questions to questionnaire association - will need the PK name
$qAssoc = $this->{'Questions'}->associations()->get('Questionnaire');
// get the existing entity to copy
$existingEntity = $this->{'Questionnaire'}->get($id, [
'contain' => ['Questions'],
]);
// clean up the existing entity
$existingEntity->unsetProperty($this->{'Questionnaire'}->getPrimaryKey());
$existingEntity->unsetProperty('created');
$existingEntity->unsetProperty('modified');
// clean up the associated records of the existing entity
foreach ($existingEntity->questions as &$q) {
$q->unsetProperty($this->{'Questions'}->getPrimaryKey());
$q->unsetProperty($qAssoc->getForeignKey());
}
// create a new entity and patch it with source data
$newEntity = $this->{'Questionnaire'}->patchEntity(
$this->{'Questionnaire'}->newEntity(),
$existingEntity->toArray()
);
// save the new entity
$result = $this->{'Questionnaire'}->save($existingEntity);
References:
https://api.cakephp.org/3.0/class-Cake.ORM.Entity.html
https://api.cakephp.org/3.0/class-Cake.ORM.Table.html
Using Symfony 2.7, I am trying to create a user registration form. I've used Symfony's own guide for this, and the form is rendered and fillable.
When I fill in the form, and press "Register", I get the following error.
Unable to find the object manager associated with an entity of class "UserBundle\Models\User"
This is triggered on the following condition in vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php at line 75
$em = $this->registry->getManagerForClass(get_class($entity));
if (!$em) {
throw new ConstraintDefinitionException(sprintf('Unable to find the object manager associated with an entity of class "%s".', get_class($entity)));
}
In the createAction in my UserController, the Entity Manager is fetched by $em = $this->getDoctrine()->getManager(); as per the instructions in the Symfony Cookbook.
I can't find out what's going here. Any help appreciated!
Also, please let me know if there is some information missing that I should have provided.
I think unless you specifically map a directory in your config, doctrine is going to look in YourBundle/Entity - this is where your User.php example diverges from the cookbook recipe.
If you want to change this you'll need to specify it explicitly as per the documentation
Alternatively just shift your User model to \Entity instead of \Models and I think it'll be OK.
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.
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.