How to render the input with VichUploaderBundle? - php

I've a trouble with the bundle VichUploaderBundle with Symfony 4.
I've follow a tutorial to make an upload with VichUploaderBundle to upload multiple files.
My problem is that the input form to upload the file is never render. But I don't see what it's wrong with my Type file.
In my MemberType who can find all fiels, I've
...
->add('member_files', CollectionType::class, [
'label' => 'Add a file',
'entry_type' => MemberFilesType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true
])
...
And my MemberFilesType is :
<?php
namespace App\Form;
use App\Entity\MemberFiles;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichImageType;
class MemberFilesType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('imageFile', VichImageType::class, [
'required' => false,
'download_uri' => true,
'image_uri' => true
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => MemberFiles::class,
]);
}
}
Do you have an idea ?

If you are working with a Member with no MemberFiles yet (empty collection) then that is what is expected. There are no collection entries because there are none persisted, try adding a new MemberFiles to the Member before you create the form.
In your controller action:
if(empty($member->getMemberFiles())){
$member->addMemberFile(new MemberFiles());
}
$form = $this->createForm(MemberType::class, $member);

Related

Accessing User Entity From Form Type in Symfony

I'm trying to build multi-tenancy support in Symfony, doing this I created a group_id column in my User, on top of that, all entities also have this same column. That way users only have access to their group's data.
I've managed to cut through this whole thing like butter with data access and display, but then came the challenge of EntityTypes for the Symfony Forms.
The question is basically, how do I make the EntityType display only the data that that particular group has inputted. Sorting it by the group_id that both the user and contact has. Whats the best way to maybe pass this in so the user only has access to their data?
<?php
namespace ContactBundle\Form;
use ContactBundle\Entity\Contact;
use ContactBundle\Entity\Organization;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class ContactType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('first_name', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label' => 'First Name',])
->add('last_name', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label'=>'Last Name',
])
->add('email', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label'=>'Email Address'
])
->add('organization_id', EntityType::Class, [
'attr' => ['class'=>'u-full-width'],
'required' => false,
'class'=>'ContactBundle:Organization',
'choice_label'=>'name',
'choice_value'=>'id',
'label'=>'Organization'
])
->add('phone', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label'=>'Phone',
])
->add('role', TextType::Class, [
'attr' => ['class'=>'u-full-width'],
'label'=>'Role',
])
->add('submit', SubmitType::class, [
'label'=>'Submit',
'attr' => [
'class'=>'button-primary',
'style'=>'margin-top:30px;',]]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class'=>Contact::class,]);
}
}
?>
It is worth noting that I am using FOSUserBundle.
In Symfony it's very easy to inject whatever you need where you need it.
// ...
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class ContactType extends AbstractType
{
private $user;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->user = $tokenStorage->getToken()->getUser();
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// ...
->add('organization_id', EntityType::Class, [
'attr' => ['class'=>'u-full-width'],
'required' => false,
'class'=>'ContactBundle:Organization',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.group', '?0')
->setParameters([$this->user->getGroup()]);
},
'choice_label'=>'name',
'choice_value'=>'id',
'label'=>'Organization'
])
// ...
If you are not using autowire and autoconfigure (Symfony 3.3+), register your form as a service manually and tag it with form.type:
# config/services.yaml
services:
AppBundle\Form\ContactType:
arguments: ['#security.token_storage']
tags: [form.type]
Related reads
https://symfony.com/doc/current/form/form_dependencies.html#define-your-form-as-a-service
https://symfony.com/doc/current/reference/forms/types/entity.html#using-a-custom-query-for-the-entities
In controller when creating form
$form = $this->createForm(ContactType::class, $contact, ['group_id' => $groupId]);
In form configureOptions method
$resolver->setDefaults(['data_class'=>Contact::class, 'group_id' => null]);
In form buildForm you can get group_id by
$options['group_id']
I got it!
So what I ended up doing was getting rid of the whole idea of using a EntityType on the form side.
I then called the data from the controller and passed it in as an option.
Like so:
$contactManager = $this->get('contact.contact_manager');
$contact = new Contact($contactManager->nextId($group = $this->getUser()->getGroupId()), $group);
$form = $this->createForm(ContactType::class, $contact, ['organizations' => $this->get('contact.organization_manager')->findDataCollection($this->getUser()->getGroupId())]);
$form->handleRequest($request);
Turned my EntityType into a choice type and passed the array of organizations into the 'choices' field as an option. Kept everything else the same like so:
->add('organization_id', ChoiceType::Class, [
'attr' => ['class'=>'u-full-width'],
'required' => false,
'choices'=> $options['organizations'],
'choice_label'=>'name',
'choice_value'=>'id',
'label'=>'Organization'
])
Then, of course, set it within the options so it can expect a variable.
$resolver->setDefaults(['data_class'=>Contact::class, 'organizations' => null]);
I appreciate all of the help and ideas!! :)
You could do this in few different ways, one was to use combination of JavaScript and EntityType.
Basically just load all the entities and hide unwanted entities with JS depending on the previous list's selection. You can use data attribute to specify things needed to make that work.

How to allow admin to add extra unmapped field to a form in real time (Symfony3)

I have a Symfony3 project which facilitates multiple admin/tenants to create custom forms for their users. TenantData is the main entity which contains Salutation, firstname and lastname fields. Based on this entity the tenant/admin can create a form TenantDataType. Here is what TenantDataType looks like:
<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Entity\TenantData;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class TenantDataType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('firstname', TextType::class, array());
$builder->add('lastname', TextType::class, array());
$builder->add('title', ChoiceType::class, array(
'choices' => array(
'Ms' => true,
'Mr' => false,
),
));
$builder->add('save', SubmitType::class, array('label' => 'Submit'));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => TenantData::class,
'csrf_protection' => true,
'allow_extra_fields' => true
));
}
}
Now I want the admin to be able to add an extra field without adding an extra column in the TenantData entity. One approach I know is to create a MetaData table which contains label, datatype, isrequired and ismultiple fields. Based on this Entity I can create a new form by the name MetaDataType. So that the admin can use this form to create an extra field. This basically means that every record of the MetaData table contains the details of an extra field. The problem is how do I use these records to render the extra fields added by the admin/tenant in real time. Since I am creating a new instance of TenantData at the time I am rendering the form and there is no TenantData id at the time which can fetch the related MetaData rows. What relationship do I need to establish to get the desired functionality.
Any ideas?
let's suppose that your formBuilder as you show above with some modification :
TenantDataType
<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Entity\TenantData;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class TenantDataType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('firstname', TextType::class, array());
$builder->add('lastname', TextType::class, array());
if(isset($option['userRole'] && $option['userRole']=="ADMIN_ROLE" ){
$builder->add('your_admin_field', TextType::class, array());
}
$builder->add('title', ChoiceType::class, array(
'choices' => array(
'Ms' => true,
'Mr' => false,
),
));
$builder->add('save', SubmitType::class, array('label' => 'Submit'));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => TenantData::class,
'csrf_protection' => true,
'allow_extra_fields' => true
));
}
}
let's suppose that a tenant user have : TENANT_ROLE
and admin has : ADMIN_ROLE
Controller
$user_role= // get the user role here . (TENANT_ROLE/ADMIN_ROLE)
$form = $this->createForm(TenantDataType ::class, $tenantObject, array(
'userRole' => $user_role,
);
if you want to access $options array from addEventListener:
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$user_role = $event->getForm()->getConfig()->getOptions()['userRole'];
}

Generate dynamic amount of CollectionType

I want to create a few CollectionType fields.
I have moderators, I load all moderators from db and want to assign sub-users to each modeator in form
Try to do something like that:
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach ($options['moderators'] as $mod) {
$builder
->add('users['.$mod->getId().']', CollectionType::class, array(
'entry_type' => UserFields::class,
'allow_add' => true,
'label' => false,
'entry_options' => array(
),
));
}
}
In $options['moderators'] there is array of moderators entities.
I want to create the same amount of CollectionType fields as moderators entities (example above - does not work because I cant pass [] as field name)
Each moderator have unique id, so I can use it and pass this id to each CollectionType but how?
How to get back this id of CollectionType in Controller function?
How to display this form on page?
I think you need to dive a little deeper into what collections are ;)
Your collection is invalid, because the entry_type should be a formType. I'm guessing in your case this should be EntityType (if you want the user to choose from a list of users).
So assuming you want a Collection of moderators with each moderator a Collection of sub-users (btw you'd normally make a CRUD and edit each moderator separately, so you don't need a collection with a sub-collection), you'd need something like this:
ModeratorCollectionType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
class ModeratorCollectionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('moderators', CollectionType::class, [
'entry_type' => ModeratorType::class,
'prototype' => true,
'allow_add' => true
]);
}
}
ModeratorType.php
<?php
namespace AppBundle\Form;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ModeratorType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('users', CollectionType::class, [
'entry_type' => EntityType::class,
'entry_options' => [
'class' => 'AppBundle:User',
],
'prototype' => true,
'allow_add' => true
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Moderator',
]);
}
}
In your Controller you'd make a form with the ModeratorCollection
$form = $this->createForm(ModeratorCollectionType::class, $data = []);

symfony3 form, can't get options in buildForm method

I don't really know which title to give this thread but, i'm working on a Symfony project in v3.1.6 and using the select2 plugin in field of my form with ajax.
I want to use the event (submit and pre_set_data) of the form component for creation and editing to change the field dynamically like this part of the symfony doc. Everything work fine until when i submit the form and give an error Notice: Undefined variable: options
My form type code here
namespace Xxx\ArticleBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Xxx\ArtistBundle\Repository\ArtistRepository;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Doctrine\ORM\EntityManager;
class ArticleType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
//$em = $options['em']; //this give me same error
$builder
->add('artist', EntityType::class, array(
'class' => 'XxxArtistBundle:Artist',
'choice_label' => 'artist_name',
'multiple' => false,
'expanded' => false))
->add('title', TextType::class)
->add('categories', EntityType::class, array(
'class' => 'XxxArticleBundle:Category',
'choice_label' => 'name',
'multiple' => true,
'expanded' => true))
->add('image', ChoiceType::class, array(
'expanded' => false,
'multiple' => false))
->add('intro', CKEditorType::class)
->add('content', CKEditorType::class);
$formModifier = function (FormInterface $form, $image) {
$listImages = $options['em']->getRepository('XxxAppBundle:Image')->find($image)); //this line give me the error
if (!$listImages) {
return; //i will add a FormError in the field
}
$listImages = array();
die(var_dump($listImages));
$form->add('image', EntityType::class, array(
'class' => 'XxxAppBundle:Image',
'choices' => $listImages,
));
};
$builder->get('image')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$image = $event->getForm()->getData();
//die(var_dump($image)); //select2 field, returned null (but it's not the question
//die(var_dump($options)); returned error 500 Notice: Undefined variable: options
$formModifier($event->getForm()->getParent(), $image);
}
);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Xxx\ArticleBundle\Entity\Article',
));
$resolver->setRequired('em');
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'xxx_articlebundle_article';
}
}
And i also want to say the goal is to have a UI similar w/ Wordpress when create a post from the dashboard, for set an image in an article and when image is selected throw it in a tag to the user and without entityType because it will have thousand so i choose an choiceType to use with the select2 plugin but if someone got a better solution i'm on it.
Thx in advance for the help
When you're using a variable from a parent scope inside a closure, you should pass it to 'use' language construct.
$formModifier = function (FormInterface $form, $image) use ($options) {
$listImages = $options['em']->getRepository('XxxAppBundle:Image')->find($image)); //this line give me the error
if (!$listImages) {
return; //i will add a FormError in the field
}
$listImages = array();
die(var_dump($listImages));
$form->add('image', EntityType::class, array(
'class' => 'XxxAppBundle:Image',
'choices' => $listImages,
));
};

Implementation of a search form with Symfony2 Form Component

Maybe i have a very dumb question, but i'm new in Symfony2 and i was wondering if i can build a Search Form with Symfony Form Component, the same way I do with a Registration Form for example.
My Search form will have a Country select field, a Club select field, a Gender radio buttons field a Level select field and the submit button.
Is it possible to do that with the Form Component or to do something like this is better to just build the search form directly in the view?
I've been searching for information about this, but I didn't find anything.
Here is how my SearchPlayerType.php looks like.
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvents;
use Doctrine\ORM\EntityRepository;
class SearchPlayersType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('country', 'entity', array(
'placeholder' => 'Choose a country',
'class' => 'AppBundle:Country',
'property' => 'name',
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('c')->orderBy('c.name', 'ASC');
},
))
->add('club', 'entity', array(
'placeholder' => 'Choose a club',
'class' => 'AppBundle:Club',
'property' => 'name',
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('c')->orderBy('c.name', 'ASC');
},
))
->add('gender', 'entity', array(
'class' => 'AppBundle:Gender',
'property' => 'name',
'expanded' => true
))
->add('level', 'rating', array(
'label' => 'Playing Level',
'stars' => 5))
;
}
public function getName()
{
return 'SearchPlayer';
}
If it is possible to do it this way I donĀ“t know what Entity my data_class needs to be
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Usuario'
));
}
}
?>
You can go two ways with this: you can either create a form specific model for your search form which will either be a really thin object with public properties; or you can remove the entry for data_class in your form options which will switch your form into returning an array rather than an object (documentation).
The former is the more OO way of doing things and allows you to add validation annotations without embedding those within the form, this way also means you can add getters and setters that transform your search data very easily and not cluttering up your controllers. So your model would look something like:
namespace MyBundle\Form\Model;
class SearchModel
{
public $country;
public $club;
// ...
}
The alternate is just:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
// don't set 'data_class' in here
));
}
Then when you do $form->getData() you'll just get an array back rather than an object.

Categories