using doctrine's dbal in symfony2 with forms - php

So I want to do the following:
I created a form class at /Form/Type/UserType.php
I have a table with a list of states (table named "states").
I want to show all of these states in a drop down menu.
What code should I use in the class UserType to show all the states?
I tried:
$request = new Request;
$conn = $request->get('database_connection');
$states = $conn->fetchAll('SELECT state_code, state_name FROM states');
$builder->add('state', 'choice', array(
'choices' => $states,
'required' => false,
));
but that gives me an error. Basically, I want to query all of the states from the table states, and create a drop down menu from all of these states.

You can create State entity, map it to states table and create a OneToMany relation with the User entity. Then in your UserType form $builder->add('state') should create dropdown field automatically. You also have to set data_class option to your User entity in getDefaultOptions method of UserType form.

#m2mdas has given you the correct object based answer. However, if all you really want to do is to store the state_code then what you have will almost work. You just need to get the connection right.
class MyType extends extends AbstractType
{
public function __construct($em) { $this->em = $em; }
public function buildForm(FormBuilder $builder, array $options)
{
$conn = $this->em->getConnection();
$statesx = $conn->fetchAll('SELECT state_code, state_name FROM states');
// Need to index for the choice
$states = array();
foreach($statesx as $state)
{
$states[$state['state_code']] = $state['state_name'];
}
$builder->add('state', 'choice', array(
'choices' => $states,
'required' => false,
));
...
// In your controller
$form = new MyType($this->getDoctrine()->getEntityManager());

Related

How to set a data by default in a drop-down list

In a project symfony3.4, I have tow entitys : Personne and Nationalite
In my form drop-down list I want select by default Nationalite 'French'.
PersonneType.php:
...
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('nationalite', EntityType::class, array(
'class' => 'AppBundle:Nationalite',
'choice_label' => 'libelle',
'required' => false,
'empty_data' => function(NationaliteRepository $repo) {
return $repo->getNationaliteParDefaut();
}
))
->add...
In NationaliteRepository.php:
...
public function getNationaliteParDefaut(){
$qb = $this->createQueryBuilder('n');
$qb->where($qb->expr()->eq('n.codeInsee', ':code_insee'))
->setParameter('code_insee', 99100); //99100 is France code_insee
return $qb->getQuery()->getOneOrNullResult();
}
...
This methode generate the following error:
Catchable Fatal Error: Argument 1 passed to
AppBundle\Form\PersonneType::AppBundle\Form{closure}() must be an
instance of AppBundle\Repository\NationaliteRepository, instance of
Symfony\Component\Form\Form given, called in /var/www/...vendor/symfony/symfony/src/Symfony/Component/Form/Form.php on line 620 and defined
The best way for entities/objects is to set the default option on the object class itself, either in the entity itself or before form creation, or using some builder/factory.
// let say thisis the class which will be filled with the natinalite
Class Person {
private $name
// ... some other fields ...
private $nationalite
public function setNationalite(Nationalite $nationale)
{
$this->nationalite = $nationalite
}
}
Form creation object method
$person = new Person();
$person->setNationalite($repo->getNationaliteParDefaut());
// lets say personType is your form, that also has field nationalite
$form = $this->createForm(PersonType:class, $person);
With this setup the form will be prepopulated with default data, from the object, including nationalite, and the user can still easilily change it.
Non object method
Another way of doing what you want is using form events.
// in your form definition
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$form->add('nationalite', EntityType::class, array(
'class' => 'AppBundle:Nationalite',
'choice_label' => 'libelle',
'required' => false,
'data' => $repo->getNationaliteParDefaut()
)
}
About empty_data
Empty data only works if the the form field is empty, eg. the user didn't select anything so the value from empty data will be used
ref: https://symfony.com/doc/current/reference/forms/types/text.html#empty-data
Try preferred_choices option https://symfony.com/doc/3.4/reference/forms/types/entity.html#preferred-choices . The preferred_choices appear on top of the list but won't be selected. If you want an item selected, build the form with a POPO (Plain Object PHP Object) with the default nationality you want.

Modifying or Override value of a posted form field in symfony 2

I have a symfony 2 form that simply posts the comment. I want to remove html characters from the posted form before saving it to database. I read somewhere that you can't directly change the value of posted form and you need to create an event listener. I have created an event listener for this purpose to achieve this goal. So i created an event listener and below is the code.
<?php
// src/Adhl/FrontBundle/EventListener/StripHtmlSubscriber.php
namespace Adhl\FrontBundle\EventListener;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class StripHtmlSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public function preSetData(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$event->getData()->setDetails(strip_tags($event->getData()->getDetails()));
$form->add('details', 'textarea', array(
'label' => false,
'attr' => array(
'cols' => '30',
'class' => 'text-input span8'
)
)
);
}
}
$event->getData()->setDetails() changes the value of posted field "details"
but $event->getData()->getDetails() does not returns anything.
I want to get the post field that has the name "details", strip html tags from it and save it back to same posted key.
In simple PHP I could do it as:
$_POST['details'] = strip_tags($_POST['details']);
Can anyone please tell me what am i doing wrong?
How does your FormType class look like?
do you bind it on a entity or array?
If you do the entity binding, then simply in the entity modify the setDetails method in your entity to
setDetails($details) {
$this->details = strip_tags($details);
}
if you have a array binding then in the action or subscriber the getData() will return an associatoive array not a entity
$data = $event->getData();
$data['details'] = strip_tags($data['details']);
$event->setData($data);

Complex data structure using only the Form component

I'm developing a e-commerce system with customizable products. Each product may have some options that, for its part, may have one or many values selected by consumer. I can't use the variant approach due the high level of customization of my users (some products could have more than 1M of variants), so I need persist the option combination chosen by costumer.
The form options are assembled dynamically once each product may have distinct options. This form should transform the user choice into a storable structure for a relational database.
Basically, it's my scenario (my attempt):
Product
Option
OptionValue
ProductOption
Order
OrderItem
OrderItemOption
Fixture:
Option: 1#Salad
Values: 1#Tomato, 2#Lettuce, 3#Pickles, 3#Carrot
Product: Hamburger
ProductOption: 1#Salad
Values: 1#Tomato, 2#Lettuce, Picles
My target is something like:
class OrderItemType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$field = $builder->create('options', new OptionPickerType(), ['options' => $options['product']->getOptions()]);
$field->addModelTransformation(new FixOptionIndexTransformer());
$builder->add($field);
}
}
class OptionPickerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach ($options['options'] as $productOption) {
$name = $productOption->getId();
$builder->add($name, 'choice', array(
'choice_list' => new ObjectChoiceList($productOption->getValues(), 'label', array(), null, 'id'),
'multiple' => true,
'cascade_validation' => true,
'property_path' => '['.$name.']'
));
}
}
}
$form = $factory->create(new OrderItemType(), ['product' => $product]);
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$item = $form->getItem(); // A collection of ItemOption filled with the OptionValue picked out from Choice field
}
}
This configuration will return a collection of arrays of OptionValue as expected. In fact it isn't enough for my purposes. What I really need is a flatten collection with all chosen values more some extra data:
class ItemOption
{
protected $item;
protected $productOption;
protected $option; // $productOption->getName()
protected $optionValue;
protected $value; // / $optionValue->getLabel()
}
As you can see, the value of Choice field in fact is inside the ItemOption.
After some days trying, I could not figure out how to do this or even think in other approach.
Can you help me?
First of all, often when I find it hard to map a form to my model, I later discover that the model is overly complicated. Simplifying the model to have clear relationships and intermediate objects where necessary (and none where not necessary) often helps here.
That being said, it seems to me that a model transformer on your option picker should do the job:
foreach ($options['options'] as $productOption) {
// ...
}
$builder->addModelTransformer(new CallbackTransformer(
// model to normalized
// needed when setting default values, not sure if required in your case
function ($modelData) {
},
// normalized to model
// converts the array of arrays of OptionValues to an array of ItemOptions
function ($normalizedData) {
$itemOptions = array();
foreach ($normalizedData as $optionValues) {
foreach ($optionValues as $optionValue) {
$itemOption = new ItemOption();
$itemOption->setProductOption($optionValue->getProductOption());
$itemOption->setOptionValue($optionValue);
$itemOptions[] = $itemOption;
}
}
return $itemOptions;
}
));

How to add some extra data to a symfony 2 form

I have a form for my entity called Book and I have a type to display a form in my view. In this type I have some fields that are mapped to properties in my entity.
Now I want to add another field which is not mapped in my entity and supply some initial data for that field during form creation.
My Type looks like this
// BookBundle\Type\Book
public function buildForm(FormBuilderInterface $builder, array $options = null)
{
$builder->add('title');
$builder->add('another_field', null, array(
'mapped' => false
));
}
The form is created like this
$book = $repository->find(1);
$form = $this->createForm(new BookType(), $book);
How can I supply some initial data now during form creation? Or how do I have to change that creation of the form to add initial data to the another_field field?
I also have a form that has fields that mostly match a previously defined entity, but one of the form fields has mapped set to false.
To get around this in the controller, you can give it some initial data pretty easily like this:
$product = new Product(); // or load with Doctrine/Propel
$initialData = "John Doe, this field is not actually mapped to Product";
$form = $this->createForm(new ProductType(), $product);
$form->get('nonMappedField')->setData($initialData);
simple as that. Then when you're processing the form data to get ready to save it, you can access the non-mapped data with:
$form->get('nonMappedField')->getData();
One suggestion might be to add a constructor argument (or setter) on your BookType that includes the "another_field" data, and in the add arguments, set the 'data' parameter:
class BookType
{
private $anotherFieldValue;
public function __construct($anotherFieldValue)
{
$this->anotherFieldValue = $anotherFieldValue;
}
public function buildForm(FormBuilderInterface $builder, array $options = null)
{
$builder->add('another_field', 'hidden', array(
'property_path' => false,
'data' => $this->anotherFieldValue
));
}
}
Then construct:
$this->createForm(new BookType('blahblah'), $book);
You can change the request parameters like this to support the form with additional data:
$type = new BookType();
$data = $this->getRequest()->request->get($type->getName());
$data = array_merge($data, array(
'additional_field' => 'value'
));
$this->getRequest()->request->set($type->getName(), $data);
This way your form will fill in the correct values for your field at rendering. If you want to supply many fields this may be an option.

Symfony2 and Doctrine2 : Use a Repository class result in Type class

I have 5 Entities:
Affiliation
Person
User
UserAffiliation
PersonAffiliation
My goal is to display a list of choice fields where I can choose all the UserAffiliations which are not in PersonAffiliations.
My idea is to create a public function in the UserAffiliationRepository which will return only those affiliations for a specific user which are not preset for a specific person.
For that, I am using:
class UserAffiliationRepository extends EntityRepository
{
public function getUnselectedAffiliations( $user_id = null, $person_id = null )
{
$commQB = $this->createQueryBuilder( 'ua' )
->select('ua');
$commQB->where("ua.user_id = {$user_id}");
$commQB->andWhere( "ua.affiliation_id not in ( select pa.affiliation_id FROM SciForumVersion2Bundle:PersonAffiliation pa where pa.person_id = 3077 )" );
return $commQB->getQuery()->getResult();
}
}
And this works fine.
Now, I would like to use this result in a FormBuilder. For that, In my controller, I am using:
$affiliations = $em->getRepository('SciForumVersion2Bundle:UserAffiliation')->getUnselectedAffiliations($user->getId(), $author->getId())
$enquiry = new PersonAffiliation();
$formType = new SubmissionAffiliationAddFormType($user, $affiliations);
$form = $this->createForm($formType, $enquiry);
And then in the Form class, I am using:
$builder->add('affiliation', 'entity', array(
'class' => 'SciForumVersion2Bundle:UserAffiliation',
'multiple' => true));
But here, I am getting all the affiliations for the specific user, not only thos ones which are not allready in the PersonAffiliations entity.
Any help? Thank you.
You have to migrate your getUnselectedAffiliations function directly into entity_type in the following way
$builder->add('affiliation', 'entity', array(
'class' => 'SciForumVersion2Bundle:UserAffiliation',
'multiple' => true,
'query_builder' = function(EntityRepository $repo) use ($yourParameters){
return $repo->createQueryBuilder(....);}));
if you want to pass $yourParameters, you have to do that into __construct function (implement it, if you don't have one) and when you create your form, you can pass them along the calls

Categories