Symfony2: $form->isValid() always returns true - php

I create a form type UserBundle/form/UserType.php.
$form->isValid() returns true if the username or the email exist even if other fields are empty.
class UserType extends AbstractType
{
private $type;
const TYPE_CREATE = 1;
public function __construct($type) {
$this->type = $type;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
switch ($this->type){
case self::TYPE_CREATE :
$builder
->add('email', 'email', array('label' => 'user.email'))
->add('username', null, array('label' => 'user.name'))
;
break;
default :
$builder
->add('username')
->add('usernameCanonical')
->add('email')
->add('emailCanonical')
->add('enabled')
->add('salt')
->add('password')
->add('lastLogin')
->add('locked')
->add('expired')
->add('expiresAt')
->add('confirmationToken')
->add('passwordRequestedAt')
->add('roles')
->add('credentialsExpired')
->add('credentialsExpireAt')
;
break;
}
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Project\UserBundle\Entity\User'
));
}
public function getName()
{
return 'project_userbundle_user';
}
}
And my controller
class UserController extends Controller
{
public function CreateAction()
{
//create user
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->createUser();
//create form
$form = $this->createForm(new UserType(UserType::TYPE_CREATE),$user);
//form submit
$request = Request::createFromGlobals();
if($request->getMethod() === "POST"){
$form->submit($request);
//test if form is valid
if($form->isValid()){
//generate random password
$password = User::randomPassword();
$user->setPassword($password);
//save user
$userManager->updateUser($user);
}
}
return $this->render('Project:User:create.html.twig',array(
'form' => $form->createView()
));
}
}

tip:
You shouldn't try to re-create the Request inside a controller. It is definitely a bad practice and can lead to undesired behavior of your application - as you're currently experiencing.
symfony will pass the request automatically to your controller actions if you add it as a method parameter:
public function createAction(Request $request)
{
// $request is holding the current request
}
Further you can get the current request from the container inside a ContainerAware class like a controller.
$request = $this->getRequest();
$request = $this->get('request');
$request = $this->container->get('request');
Further please stick with the symfony coding standards. There are multiple violations in your code.
Method names should be lower camelCased not CamelCased.
UserBundle/form should be UserBundle/Form.
A method's opening curly brackets/braces belong to the next line.
public function __construct($type) // {
{
$this->type = $type;
}

You have to use Validation Constraints NotNull or NotBlank ( http://symfony.com/doc/current/reference/constraints.html ).
My guess is that, default "required=true" on fields isn't working because You are recreating request.

Related

symfony4 inject parameter in form

I have a Business Entity and a BusinessObject Entity, and I would like to link the BusinessObject to the current Business when I create a new BusinessObject.
For example, if my route is business/{id}/object/new, I would like to have the object related with the Business (thanks to the id).
In my BusinessObject Controller, I managed to use #ParamConverter to get the Business id.
In my BusinessObject Form, I put an HiddenType to my business entry because I don't want it to appear, and set data to business_ID.
I struggle in configureOptions to get the business ID, I can't figure out how to get the business id from here.
BusinessObject Controller (route new):
/**
* #Route("/{post_id}/new", name="business_object_new", methods="GET|POST")
* #ParamConverter("business", options={"id" = "post_id"})
*/
public function new(Request $request,Business $business): Response
{
$businessObject = new BusinessObject();
$businessID = $business->getId();
$form = $this->createForm(BusinessObjectType::class, $businessObject,array(
'business_ID'=>$businessID,
));
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($businessObject);
$em->flush();
return $this->redirectToRoute('business_object_index');
}
return $this->render('business_object/new.html.twig', [
'business_object' => $businessObject,
'business'=>$business,
'form' => $form->createView(),
]);
}
BusinessObjectType:
class BusinessObjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('object',TextType::class)
->add('complement')
->add('status')
->add('durationExpected')
->add('durationAchieved')
->add('client')
->add('projectManager')
->add('business',HiddenType::class,array(
'data' => $options['business_ID']
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => BusinessObject::class,
'business_ID'=>Business::class
]);
}
}
With this code, I get an error Expected argument of type "App\Entity\Business or null", "string" given. I think this have something to do with the function configureOptions() in my Form
The approach can be:
public function new(Request $request,Business $business): Response
{
$businessObject = new BusinessObject();
$form = $this->createForm(BusinessObjectType::class, $businessObject);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// I suppose your setter is `setBusiness`, otherwise use more suitable one
$businessObject->setBusiness($business);
$em = $this->getDoctrine()->getManager();
$em->persist($businessObject);
$em->flush();
Form builder is:
builder
->add('object',TextType::class)
->add('complement')
->add('status')
->add('durationExpected')
->add('durationAchieved')
->add('client')
->add('projectManager'); // No business field
Another option is to embed BusinessType form into BusinessObjectType, you can read more about form embedding here.

ManyToMany relations in symfony doctrine

i have a Agency Entity
with a fields $owns
looks like
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\ShipType", inversedBy="owners")
*/
protected $owns;
on ShipType Entity
i have
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Agency", mappedBy="owns")
*/
private $owners;
doctrine created a relations table for association between tables with agency_id and ship_type_id
i'm trying to get a form to work for assign each agency to a ship type ( owns )
im trying to achieve logging as an agency
so far i got
public function gShips(Request $request): Response {
$u = $this->getUser();
$ag = new Agency();
$form = $this->createForm(ChooseShipsType::class, $ag);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$a = $form->getData();
$em->persist($a);
$em->flush();
}
return $this->render('agency/sships.html.twig', [
'adForm' => $form->createView()
]);
}
and the form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('ships', EntityType::class, [
'class' => 'AppBundle:ShipType',
'expanded' => true,
'multiple' => true,
])
->add('save', SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Agency'
]);
}
the form is showing, but can't get it to persist because it's trying to create a new Agency, i can't figure out how to use the relation table between these two tables
thank you in advance
Check the constructor of your Agency class. Make sure it has the following:
$this->owns = new ArrayCollection();
And then, make sure you have an addOwns() method:
public function addOwns(ShipType $shipType)
{
$this->owns[] = $shipType;
}
And also a setter:
public function setOwns($owns)
{
if ($owns instanceof ArrayCollection) {
$this->owns = $owns;
} else {
if (!$this->owns->contains($owns)) {
$this->owns[] = $owns;
}
$this->owns;
}
return $this;
}
Also, make sure you have the getter with the default content. That should do.
PS: You shouldn't name your properties as verbs though, but that's another thing.

Symfony 2 prefilled form with data from the database

I am trying to populate form fields with data from the database in order to edit them. I already searched on google.
Here is my controller which returns empty fields
public function userViewAction($id,Request $request){
$em = $this->getDoctrine()->getManager()->getRepository('BFVMailingBundle:MailingList');
$user = $em->findById($id);
$form = $this->get('form.factory')->createBuilder('form',$user)
->add('unsubscribed','checkbox')
->add('name','text')
->add('givenName','text')
->add('additionalName','text',array('required'=>false))
->add('familyName','text',array('required'=>false))
->add('emailValue','text')
->add('language','choice',array(
'choices' => array('en_GB' => 'en_GB', 'es_ES' => 'es_ES', 'fr_FR' => 'fr_FR'),
'required' => true,
))
->add('commentary','textarea',array('required'=>false))
->add('save','submit')
->getForm();
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// perform some action, such as save the object to the database
$em->flush();
return $this->redirect($this->generateUrl('user_view',array('id'=>$id)));
}
}
and this is my template
<div class="cell">
{{ form_start(form, {'attr': {'class': 'form-horizontal'}}) }}
{{ form_end(form) }}
</div>
Did I miss something?
EDIT - READ THIS FOR THE SOLUTION
As John Noel Implied I build an externalised form with the command
php app/console doctrine:generate:form BFVMailingBundle:MailingList
my entity was MailingList instead of User
the MailingListType is a form template which is generated in BFV\MailingBundle\Form. I've added the data types myself.
<?php
namespace BFV\MailingBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class MailingListType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$str = date("U");
$codeValue = sha1($str);
$builder
->add('secretCode','hidden',array( 'data' => $codeValue ))
->add('name','text')
->add('givenName','text')
->add('additionalName','text',array('required'=>false))
->add('familyName','text',array('required'=>false))
->add('emailValue','text')
->add('language','choice',array(
'choices' => array('en_GB' => 'en_GB', 'es_ES' => 'es_ES', 'fr_FR' => 'fr_FR'),
'required' => true,
))
->add('unsubscribed','checkbox')
->add('commentary','textarea',array('required'=>false))
->add('save','submit')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BFV\MailingBundle\Entity\MailingList'
));
}
/**
* #return string
*/
public function getName()
{
return 'bfv_mailingbundle_mailinglist';
}
}
In the reformated controller I add to add the the form generator the instance of MailingList $user[0] instead of $user. I read in many websites that usually you put $variable directly in the form builder but that generated the following error:
The form's view data is expected to be an instance of class
BFV\MailingBundle\Entity\MailingList, 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
BFV\MailingBundle\Entity\MailingList
Thus in the controller:
public function userViewAction($id,Request $request){
if (!$id) {
throw $this->createNotFoundException('No id !!');
}
$em = $this->getDoctrine()->getManager()->getRepository('BFVMailingBundle:MailingList');
$user = $em->findById($id);
if (!$user){
throw $this->createNotFoundException('No user with the id selected');
}
$form = $this->createForm(new MailingListType(), $user[0]);
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
$em->flush();
return $this->redirect($this->generateUrl('user_view',array('id'=>$id)));
}
}
return $this->render('BFVMailingBundle:Default:user_view.html.twig',array(
'user'=>$user,
'form'=>$form->createView()
));
}
Conclusion: I got the view form rendering with populated data from the database.
To do this you'll want to look into form classes which will then act as a view (and will also populate) the data you provide. So in your example you'd create a form class UserType:
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('unsubscribed','checkbox')
->add('name','text')
->add('givenName','text')
->add('additionalName','text',array('required'=>false))
->add('familyName','text',array('required'=>false))
->add('emailValue','text')
->add('language','choice',array(
'choices' => array('en_GB' => 'en_GB', 'es_ES' => 'es_ES', 'fr_FR' => 'fr_FR'),
'required' => true,
))
->add('commentary','textarea',array('required'=>false))
->add('save','submit')
;
}
public function getName()
{
return 'user';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Your\Entity\Class',
));
}
}
Then within your controller you'd do something along the lines of:
$form = $this->createForm(new UserType(), $user);
Then the rest of your controller as you have. Definitely read up on form classes though as that's the starting point for a lot of the advanced functionality of Symfony forms.
I'm not sure if this is what makes the difference, but did you try with:
$form = $this->createFormBuilder($user)
->add(...)
->getForm()
You may also check that the User you get is correctly read from the DB.
For example:
if (!is_object($user)) {
$this->createNotFoundException('The user does not exist');
}

Symfony2 custom form type key pair

I'm currently trying to implement a key-pair value for a form type, which is used together with the FOSRestBundle to allow for sending a request like the following:
{
"user": {
"username": "some_user",
"custom_fields": {
"telephone": "07777",
"other_custom_field": "other custom value"
}
}
}
The backend for this is represented as follows:
User
id, username, customFields
CustomUserField
id, field
CustomUserFieldValue
user_id, field_id, value
I've currently made a custom form as follows:
<?php
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username')
->add(
'custom_fields',
'user_custom_fields_type'
)
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Acme\DemoBundle\Entity\User',
'csrf_protection' => false,
)
);
}
public function getName()
{
return 'user';
}
}
And my user_custom_fields_type:
<?php
class CustomUserFieldType extends AbstractType
{
private $em;
/**
* #param EntityManager $em
*/
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$fields = $this->em->getRepository('AcmeDemoBundle:CustomUserField')->findAll();
foreach($fields as $field) {
$builder->add($field->getField(), 'textarea');
}
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'invalid_message' => 'The selected custom field does not exist'
)
);
}
public function getParent()
{
return 'collection';
}
public function getName()
{
return 'user_custom_fields_type';
}
}
This keeps giving me the error that there are extra fields. Which are the ones I've added in the CustomUserFieldType. How can I get this working?
Note: this is a simplified version of the actual code, I've tried removing all the irrelevant code.
You need to use form listeners to add dynamic fields to your forms:
http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html
In your case a PRE_SET_DATA should suffice. Something like this:
$em = $this->em;
$builder->addEventListener(FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ( $em )
{
// get the form
$form = $event->getForm();
// fetch your data here
$fields = $em->getRepository('AcmeDemoBundle:CustomUserField')->findAll();
foreach($fields as $field)
{
// make sure you add the new fields to $form and not $builder from this event
$form->add($field->getField(), 'textarea');
}
});
I had exactly the same issue and finally solved it by parsing the custom fields manually. If there is another solution, please share :)
In the UserType form:
$builder->addEventListener(FormEvents::POST_SET_DATA,
function (FormEvent $event) use ( $oEm, $oUser )
{
$oForm = $event->getForm();
$aFields = $oEm->getRepository('MyDBBundle:CustomUserField')->findAll();
/** #var CustomUserField $oField */
foreach($aFields as $oField)
{
$oForm->add(
'custom__'.$oField->getKey(),
$oField->getType(),
array(
'label' => $oField->getField(),
'mapped' => false,
'required' => false
)
);
/** #var CustomUserFieldValue $oFieldValue */
$oFieldValue = $oEm->getRepository('MyDBBundle:CustomUserFieldValue')->findOneBy(array('user' => $oUser, 'field' => $oField));
if(null !== $oFieldValue) {
$oForm->get('custom__' . $oField->getKey())->setData($oFieldValue->getValue());
}
}
}
);
Then, in your controller action which handles the request of the submitted form:
// Handle custom user fields
foreach($oForm->all() as $sKey => $oFormData)
{
if(strstr($sKey, 'custom__'))
{
$sFieldKey = str_replace('custom__', '', $sKey);
$oField = $oEm->getRepository('MyDBBundle:CustomUserField')->findOneBy(array('key' => $sFieldKey));
/** #var CustomUserFieldValue $oFieldValue */
$oFieldValue = $oEm->getRepository('MyDBBundle:CustomUserFieldValue')->findOneBy(array('user' => $oEntity, 'field' => $oField));
if($oFieldValue === null)
{
$oFieldValue = new CustomUserFieldValue();
$oFieldValue->setUser($oEntity);
$oFieldValue->setField($oField);
}
$oFieldValue->setValue($oFormData->getData());
$oEm->persist($oFieldValue);
}
}
(Assuming that there is both a "field" property and a "key" in the CustomUserField entity; key is a unique, spaceless identifier for your field and field is the human friendly and readable field label.)
This works so hope it can be helpful. However, wondering if someone has a better solution. :)

How to make 2 forms in one view dealing with the same entity but with only one bind to it?

Here is the thing.
I want to make 2 forms in one view. One is bound to an entity and the other is a file type where I want to put .csv and catch the informations inside to fill the first entity.
I've already created the first one but I can't figure how to create the second and integrate it correctly in the same view so they don't fight each other. Here is my files (the csv process is not here yet)
My controller:
public function adminAction()
{
$form = $this->createForm(new StudentsType, null);
$formHandler = new StudentsHandler($form, $this->get('request'), $this->getDoctrine()->getEntityManager());
if( $formHandler->process() )
{
return $this->redirect( $this->generateUrl('EnsgtiEnsgtiBundle_ens'));
}
return $this->render('EnsgtiEnsgtiBundle:Appli:admin.html.twig', array(
'form' => $form->createView(),
));
}
First form:
class StudentsType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('nom', 'text')->setRequired(false)
->add('prenom', 'text')
->add('email', 'email')
->add('codeEtape', 'text')
->add('file', new StudentsListType)->SetRequired(false);
}
public function getName()
{
return 'ensgti_ensgtibundle_studentstype';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Ensgti\EnsgtiBundle\Entity\Students',
);
}
}
The Handler:
class StudentsHandler
{
protected $form;
protected $request;
protected $em;
public function __construct(Form $form, Request $request, EntityManager $em)
{
$this->form = $form;
$this->request = $request;
$this->em = $em;
}
public function process()
{
if( $this->request->getMethod() == 'POST' )
{
$this->form->bindRequest($this->request);
if( $this->form->isValid() )
{
$this->onSuccess($this->form->getData());
return true;
}
}
return false;
}
public function onSuccess(Students $students)
{
$this->em->persist($students);
$this->em->flush();
}
}
Second Form:
class StudentsListType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('', 'file');
}
public function getName()
{
return 'ensgti_ensgtibundle_studentslisttype';
}
}
The Handler:
class StudentsListHandler
{
protected $form;
protected $request;
protected $em;
public function __construct(Form $form, Request $request, EntityManager $em)
{
$this->form = $form;
$this->request = $request;
$this->em = $em;
}
public function process()
{
if( $this->request->getMethod() == 'POST' )
{
$this->form->bindRequest($this->request);
if( $this->form->isValid() )
{
//$this->onSuccess($this->form->getData());
print_r($this->form->getData());
return true;
}
}
return false;
}
public function onSuccess(/*StudentsList $studentsList*/)
{
//$this->em->persist($studentsList);
//$this->em->flush();
echo 'Petit test';
}
}
With this code I get this:
Neither property "file" nor method "getFile()" nor method "isFile()" exists in class "Ensgti\EnsgtiBundle\Entity\Students"
Which is normal since I'm bind to an entity with no file type. I'm a beginner so I keep reading docs but it's still hard for me to understand everything. Is there a way I can make 2 forms in the same view with one that is not bind to an entity but that will persist on it anyway via csv treatment??
The problem is that the form expects file to be a property of the bound entity. You just need to set the property_path to false. (Reference) e.g.
->add('file', new StudentsListType, array('property_path' => false))
Edit:
If it would make more sense to have the 2 forms completely separate, then remove the above ->add(/*...*/) entirely and just pass both forms to the view from the controller. You can then render each one individually.
use <iframe> to do that. Here is the link how to use it, just put in src atribute url to needed page. And you may display 2 pages in one layout. And this is how it looks

Categories