separate one form into 2 independent forms in symfony FOS - php

I created a login system in symfony with the possibility to register only for those who own a so called "client number", right now my problem is the registration form where user has basically to type in at first his client number which is going to be validated first if it exists in a database and then after submitting it, orm fetches some data about the company which possesses the client number and the application forwards user to another form where he has to type in other details like his username and password. So far right now I have it in one form and I created it after as being described in tutorial like with Registration Controller, Registration Form Handler and Registration Type, right now for example in Registration Type it looks like this
<?php
// src/AppBundle/Form/RegistrationType.php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
class RegistrationType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('name');
// $builder->add('ClientNr'); <-- I want to check it in another form before i come to registration
$builder->add('lanr');
$builder->add('personal_key', HiddenType::class, array(
'data' => $this->getID()));
}
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
// Or for Symfony < 2.8
// return 'fos_user_registration';
}
public function getBlockPrefix()
{
return 'app_user_registration';
}
// For Symfony 2.x
public function getName()
{
return $this->getBlockPrefix();
}
public function getID()
{
return $random = random_int(10000,99999);
}
as far as I know I have again to create files like "PreRegistration Controller, PreRegistrationFormHandler and PreType" or what are the exact steps to realize my idea?

Related

Decoupled way of modifying forms in Symfony 3/4

I'd like to be able to alter a form using the Symfony Event Dispatcher. The Symfony documentation talks about dynamic form modification, however, in all examples the event listener or subscriber is created in the form class. I'd like to keep this logic decoupled from my form class.
How can I modify a Symfony form without having to specify which event listeners are going to be called in the form class?
Probably what you need is a form type extension, it allow you to modify any existing form types across the entire system. There, you can add event listeners/subscribers or what you want to any specific or generic form type.
However, this task tends to get tedious if it's a very frequent case. So doing something like this can provide you with a perfect fit:
class FoobarFormSubscriber implements EventSubscriberInterface, FormEventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public static function getFormClass()
{
return FoobarType::class;
}
public function preSetData(FormEvent $event)
{
$form = $event->getForm();
$form->add('custom', null, array('mapped' => false));
}
}
But obviously this isn't a feature implemented by Symfony. Here I leave you a recipe to achieve it:
First, create a new form type extension to add the subscriber to the form builder according to configuration:
class FormEventTypeExtension extends AbstractTypeExtension
{
private $subscribers;
public function __construct(array $subscribers = array())
{
$this->subscribers = $subscribers;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$formClass = get_class($builder->getType()->getInnerType());
if (isset($this->subscribers[$formClass])) {
foreach ($this->subscribers[$formClass] as $subscriber) {
$builder->addEventSubscriber($subscriber);
}
}
}
public function getExtendedType()
{
return FormType::class;
}
}
Create a new interface to configure the form class to listen to:
interface FormEventSubscriberInterface
{
public static function getFormClass();
}
Finally, into a new compiler pass, injects to the extension service all registered kernel.event_subscriber that implement the previous interface:
public function process(ContainerBuilder $container)
{
$subscribers = array();
foreach ($container->findTaggedServiceIds('kernel.event_subscriber') as $serviceId => $tags) {
$subscriberClass = $container->getDefinition($serviceId)->getClass();
if (is_subclass_of($subscriberClass, FormEventSubscriberInterface::class, true)) {
$subscribers[$subscriberClass::getFormClass()][] = new Reference($serviceId);
}
}
$extensionDef = $container->getDefinition(FormEventTypeExtension::class);
$extensionDef->setArgument(0, $subscribers);
}
Then, your custom subscribers are decoupled and ready to work as is, just make sure to implement both interfaces (EventSubscriberInterface, FormEventSubscriberInterface) and register the event subscriber as service.

handle which form is submitted and persist entity after findAll

I'll try to explain myself (my English is not as good as I wish)
I'm very inexperienced with symfony2 and doctrine2.
I'm trying to create a twig template and all the logic to handle modifications in, for example, user entity.
I've made the UserType AbstractType class and can handle, modify and persist if I get just one record or, at least, if I show only one form.
I've tried to do the same thing showing to the user every "User" in my database, and allowing him to modify and save one of them each time by clicking in submit button.
What I have right now:
src/Dummy/Form/Type/UserType.php
namespace Dummy\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setAction('')
->setMethod('POST')
->add('Name')
->add('Adress')
->add('save')
;
}
}
src/Dummy/Test/Controller/TestController.php
namespace Dummy\TestBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Dummy\TestBundle\Entity\Users;
use Dummy\Form\Type\UserType;
class ArticuloController extends Controller
{
public function indexAction(Request $request)
{
//some stuff
$users= $this->getDoctrine()
->getRepository('DummyTestBundle:Users')
->findAll();
$forms = array();
foreach($users as $user)
{
$forms[] = $this->createForm(UserType::class, $user); //*1
}
//... Don't know how to handle request with $forms[index]->submit(...
//... Check if is valid
//... set values
//... persist
$twigForms = array();
foreach($forms as $form)
{
$twigForms[] = $form->createView()
}
//... render twig template
}
}
Also I have the entity Users which works fine, made from yaml config file as described in the documentation
What I want
Handle the request and persist the object modifications.
The part that works
Until clicking in that submit button this works fine, it shows a template with a form for each user in database, every one of them with his submit button. After pressing any of them I'm completely lost.
If I force index to be 0, it works too (but only in the first form).
Because of that, I think that I need to know the index in $users or $forms variable (marked with *1 commented point above) in order to handle that request using something like:
$forms[index]->submit($request->request->get($forms[index]->getName()));
if ($forms[index]->isValid()) {
$users[index]->setName('Name from post');
$users[index]->setAdress('Adress from post');
$em = $this->getDoctrine()->getManager();
$em->persist($users[index]);
$em->flush();
}
But don't know if this is correct or how to do it.
I also read about collections, but don't know how to make this work with them.

Yii Validate an un-bound Variable (non-stored)

Classic problem:
verify that a user accepted the contract terms but the value of the acceptance is not stored (bound) in the database...
Extend CFormModel rather than CActiveForm (because CActiveForm binds
values to DB)
Post a CFormModel to a controller action
Validate a CFormModel
I'm asking this question to answer it because the existing questions end in see the documentation...
extend CFormModle, define the rules and got to validate. With bound variables you validated as part of save. Now you validate() by itself but Validate requires a list of attributes which is not defined in CFormModel. So, what do you do? You do this:
$contract->validate($contract->attributeNames())
Here's the full example:
class Contract extends CFormModel
{
...
public $agree = false;
...
public function rules()
{
return array(
array('agree', 'required', 'requiredValue' => 1, 'message' => 'You must accept term to use our service'),
);
}
public function attributeLabels()
{
return array(
'agree'=>' I accept the contract terms'
);
}
}
Then in the controller you do this:
public function actionAgree(){
$contract = new Contract;
if(isset($_POST['Contract'])){
//$contract->attributes=$_POST['Contract']; //contract attributes not defined in CFormModel
...
$contract->agree = $_POST['Contract']['agree'];
...
}
if(!$contract->validate($contract->attributeNames())){
//re-render the form here and it will show up with validation errors marked!
}
The results:

Remove form namespace in Symfony2 form (for REST API)

I'm designing REST API with Symfony2.
For POST and PUT request i'm using a FormType. Something like :
class EmailType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('subject', 'textarea')
[...]
;
}
public function getName()
{
return 'email';
}
}
But when I POST, i'm must pass fields with a namespace like :
{
"email": {
"subject": "subject"
}
}
But I don't want this top-level namespace !
Any ideas ?
A form type has to have a name because if you register it as a service tagged as a form type, you need to somehow reference it. In the following code snippet, email is the name of the form type:
$form = $this->formFactory->create('email', $email);
That's why you have to return a name in the form type class:
public function getName()
{
return 'email';
}
So, instead of creating a form type without a name, just create a form — a particular instance of that form type — with an empty name:
$form = $this->formFactory->createNamed(null, 'email', $email);
An empty string — '' — instead of null works as well.
I've used Symfony forms for JSON based APIs. You just need to change your getName() method to return '':
public function getName()
{
return '';
}
This, cobined with the FOSRestBundle, made working with POSTed data very easy.

Custom form type symfony

I need to set up a custom form type in Symfony that uses the choice type as a parent but doesn't actually require choices to be preloaded. As in I want to be able to populate the select with an ajax call and then submit with one of the options from the call without getting This value is not valid. errors, presumably because its not one of the preloaded options.
I don't need a custom data transformer as I am doing that through the bundle controller, I just need Symfony not to complain when I submit with an option that wasn't originally on the list. Here is what my custom form type looks like so far:
<?php
namespace ISFP\Index\IndexBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class NullEntityType extends AbstractType
{
public function getDefaultOptions(array $options)
{
$defaultOptions = array(
'em' => null,
'class' => null,
'property' => null,
);
$options = array_replace($defaultOptions, $options);
return $options;
}
public function getParent()
{
return 'choice';
}
public function getName()
{
return 'null_entity';
}
}
Dude look at the EntityType it has a parent as a choice. But entire display was handle by ChoiceType. When I was doing similar things I've started from overload Both ChoiceType and EntityType. And then set in overloaded Entity the getParent() to mine overloaded choice.
Finally In my case I modify the new choice and put there my embedded form. It's tricky to do It. And it consumes lot's of time.
But with that approach i don't have any problem with Validation.

Categories