I want to patch some data in my api-platform api using symfony forms. Before that i need to generate a form which should get filled form with actual data.
Is this a good way to do it?
class CruderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class)
->add('amount', IntegerType::class)
->add('save', SubmitType::class)
;
}
}
public function editProduct($id, Request $request) {
$client = new CurlHttpClient();
$data = array($client->request('GET', 'https://api'));
// url return: [{"id":1,"name":"testname","amount":2}]
$form = $this->createForm(CruderType::class, $data);
return $this->render('product/edit_product.html.twig', [
'form' => $form->createView()
]);
}
It returns form but fields not filled by data from endpoint.
(Yes, I know it won't send, it'll be done later)
Related
I want to create a form in Symfony. The form needs a collection of task fields which are loaded from the database. Every single field should have a dropdown to select a person who is supposed to do this task.
What I want to do is to map those two entities together. What I have so far is this:
createFormType.php
class CreateFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add("dateValidFrom", DateTimeType::class)
->add("dateValidUntil", DateTimeType::class)
->add("generalTasks", CollectionType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
"data_class" => DTOPlan::class
]);
}
}
DTOPlan.php
class DTOPlan
{
public \DateTimeImmutable $dateValidFrom;
public \DateTimeImmutable $dateValidUntil;
public array $tasks;
public function setGeneralTasks(array $generalTasks): void
{
$this->generalTasks = array_fill_keys($generalTasks, null);
}
...getter and setter
}
Controller.php
public function newPlan(Request $request)
{
$generalTasks = $this->generalTaskRep->findAll();
$dtoPlan = new DTOPlan();
$dtoPlan->setGeneralTasks($generalTasks);
$form=$this->createForm(CreateFormType::class, $dtoPlan);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid())
{
$this->planRepository->updatePlan($form->getData());
$this->redirectToRoute("app.plan.show");
}
return $this->render("/Plan/plan_create.html.twig", [
"createForm" => $form->createView()
]);
}
The labels are now correct: Form
Now I want to insert a dropdown into them for selecting firstName and lastName of Person
How can I manage that?
I'm actually creating a website (with symfony 3) where the login form is on the main page (route /). And I would like to handle this form on the /login route.
Unfortunately I don't know how to do that because my form is built in the indexAction() and my loginAction() has no visibility on the $form built in index...
/**
* #Route("/", name="home")
*/
public function indexAction()
{
$user = new User();
$form = $this->createFormBuilder($user)
->setAction($this->generateUrl('login'))
->setMethod('POST') //btw is this useless ?? Is it POST by default ?
->add('Login', TextType::class)
->add('Password', TextType::class)
->add('save', SubmitType::class, array('label' => 'Sign In'))
->getForm();
return $this->render('ShellCodeHomeBundle:Home:index.html.twig', array (
'login' => '',
'form_signin' => $form->createView(),
));
}
/**
* #Route("/login", name="login")
*/
public function loginAction(Request $request)
{
$user = new User();
//how do I handle the form ????
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$user = $form->getData();
//...
}
return $this->render('ShellCodeHomeBundle:Home:index.html.twig', array (
'login' => $user->getLogin(),
));
}
I guess it's useless to tell that, but I'm using twig and I insert the form like this :
<div class="col-lg-12" style="text-align: center;">
{{ form_start(form_signin) }}
{{ form_widget(form_signin) }}
{{ form_end(form_signin) }}
</div>
Hope you will able to help me ! Thanks !
Do not build your form within the controller, that's a bad practice, because you can't reuse this code.
You should create your form within FormType and define them as a service. That way, you'll be able to reuse this form as often as you need it to.
Taken from the docs linked below, here's an example for the FormType:
namespace AppBundle\Form;
use AppBundle\Entity\Post;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
class PostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('summary', TextareaType::class)
->add('content', TextareaType::class)
->add('authorEmail', EmailType::class)
->add('publishedAt', DateTimeType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Post::class,
));
}
}
Then you could use the FormType as in this example:
use AppBundle\Form\PostType;
// ...
public function newAction(Request $request)
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
// ...
}
For detailed information, check the docs
Create a custom form type.
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\{
PasswordType, SubmitType, TextType
};
use Symfony\Component\Form\FormBuilderInterface;
class LoginForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('Login', TextType::class)
->add('Password', PasswordType::class)
->add('save', SubmitType::class, [
'label' => 'Sign In'
])
;
}
}
Here is the answer :
In your other function just build the same form, handle the request and check if it's valid in this second method.
#[Route('/url', name: 'app_url')]
public function index(Request $request, EntityManagerInterface $em) : Response {
$entity = new Entity();
$data = 'some data';
$form = $this->createForm(EntityFormType::class, $entity, [ 'data' => $data, 'action' => 'other_url' ]);
/** extra code and send the request **/
}
then in the second controller function :
#[Route('/otherUrl', name: 'other_url')]
public function validation(Request $request, EntityManagerInterface $em) : Response {
/** SAME FORM CODE HERE **/
$entity = new Entity();
$form = $this->createForm(EntityFormType::class, $entity, [ 'data' => $data, 'action' => 'other_url' ]);
/** extra code and send the request **/
}
The form will be received and the framework will understand it correctly, it's like it's the same form, each form data will be put in the right place in the new form instance faking it's the same.
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');
}
I am starting with coding in Symfony and I have the following problem:
Assume I have two Entities 'Client' and 'Project'. They are stored with Doctrine.
A Client has a Id, Name and an Email
A Project has a Id, client_id, name
So basically a project belongs to a Client and a Client has many projects.
My problem now:
When I'm creating a project, I want a dropdown with all possible clients. As I might be using a client drowpown somewhere else in my project I'm asking myself if there is a smart way to something like this:
class ProjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'text');
$builder->add('client', new ClientListType());
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Project'
));
}
public function getName()
{
return 'project';
}
}
class ProjectController extends Controller
{
public function createAction(Request $request)
{
$project = new Project();
$options = array( ... );
$form = $this->createForm(new ProjectType(), $project, $options);
$form->handleRequest($request);
if($form->isValid()){
// persist project
return $this->redirectToRoute('show_projects');
}
return $this->render('AppBundle:Client:create.html.twig', array(
'form' => $form->createView()
));
}
}
Where ClientListType adds a select statement for all possible Clients to the form.
And $form->isValid() checks if the client (id) is valid or not.
At the moment I have the followin code in ProjectType to generate the dropdown entries:
function __construct($clients)
{
$this->clients = $clients;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'text');
$builder->add('client', 'choice', array(
'choices' => $this->buildChoices()
));
}
public function buildChoices()
{
$res = array();
foreach ($this->clients as $client) {
$res[$client->getId()] = $client->getName();
}
return $res;
}
But I'm assuming there is a much better way to do this, because this seems like a common problem.
What I would do is simply this :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'text')
->add('client', 'entity', array(
'class'=>'AppBundle\Entity\Client',
'property'=>'name'
));
}
Hope this helps.
I have a contact form that works fine. But I want to fill some of this fields when an user click in one link. I receipt the information of some fields in the form controller, but I don't know how can I show this form with this fields with these datas.
Thanks!
You can pass an object, which corresponds to your data fields, when you create your form :
In your controller:
$task = new Task();
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit')
->getForm();
Or if you have your own FormType class, bind it to your Entity :
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Build form here
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
// Bind entity here
$resolver->setDefaults(array(
'data_class' => 'BlueLinea\Bundle\BlueHomeCareBundle\Entity\MyEntity',
));
}
}
And in your controller :
$myEntity = new MyEntity();
$form = $this->createForm(new MyFormType(), $myEntity);
See Forms (Building the Form) - Symfony