I have this form and I have to get the values of 3 fields (name and 2 dates) separately, formate the dates and put them into an arrayCollection.
I only want to get these 3 fields and let the rest of the form field get inserted automatically as usual.
In parallel, when I come back to edit the form, I want to know how to distribute these values to populate the form.
According to this documentation creating-form-classes , an attempt of solution is to do something like :
We assume that your have an entity name Article.
Your form can be something like :
class ArticleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title', TextType::class)
->add('author', TextType::class)
->add('name', TextType::class, [
'mapped' => false
])
->add('date1', DatetimeType::class, [
'mapped' => false
])
->add('date2', DatetimeType::class, [
'mapped' => false
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Article::class,
]);
}
}
Here there is option 'mapped' => false because you don't want to mappe it with the entity Article.
Then in your controller, you can have something like
/**
* #Route("/articles", methods={"POST", "GET"}, name="app_post_article")
*/
public function postArticle(Request $request, EntityManagerInterface $em)
{
$form = $this->createForm(ArticleType::class);
$form->handleRequest($request);
if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) {
$name = $form->get('name')->getData();
$date1 = $form->get('date1')->getData();
$date2 = $form->get('date2')->getData();
//.... do something
}
//... Do other thing
}
/**
* #Route("/articles/{id}", methods={"POST", "GET"}, name="app_edit_article")
*/
public function editArticle(Request $request, Article $article, EntityManagerInterface $em)
{
$form = $this->createForm(ArticleType::class, $article);
// $prevName, $prevDate1, $prevDate2 must be retreive first...
$form->get('name')->setData($prevName);
$form->get('date1')->setData($prevDate1);
$form->get('date2')->setData($prevDate2);
$form->handleRequest($request);
if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) {
$name = $form->get('name')->getData();
$date1 = $form->get('date1')->getData();
$date2 = $form->get('date2')->getData();
//.... do something
}
//... Do other thing
}
It's just an idea.
Related
Suppose I have two entities: a post and a comment. Each post can have many comments. Now, suppose I have a comment form. It is supposed to take user input and store it in the database.
Simple stuff. At least, it should be, but I can't get it to work.
How do I refer to the post (parent) when creating the comment (child)? I tried manually passing the post_id to the comment form as a hidden field, but received an error complaining about how the post ID is a string.
Expected argument of type "App\Entity\Post or null", "string" given.
Here is my code so far. Can someone nudge me into the right direction?
CommentType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$post_id = $options['post_id'];
$builder->add('content', TextareaType::class, [
'constraints' => [
new Assert\NotBlank(['message' => 'Your comment cannot be blank.']),
new Assert\Length([
'min' => 10,
'minMessage' => 'Your comment must be at least {{ limit }} characters long.',
]),
],
])->add('post', HiddenType::class, ['data' => $post_id]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Comment::class,
'post_id' => NULL,
]);
}
PostController.php (this is where the comment form appears)
// Generate the comment form.
$comment = new Comment();
$form = $this->createForm(CommentType::class, $comment, [
'action' => $this->generateUrl('new_comment'),
'post_id' => $post_id,
]);
CommentController.php
/**
* #param Request $request
* #Route("/comment/new", name="new_comment")
* #return
*/
public function new(Request $request, UserInterface $user)
{
// 1) Build the form
$comment = new Comment();
$form = $this->createForm(CommentType::class, $comment);
// 2) Handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
// 3) Save the comment!
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($comment);
$entityManager->flush();
}
return $this->redirectToRoute('homepage');
}
Thank you very much for your help!
You just need to pass the actual Post entity, not just the id. Try this:
CommentController.php
public function new(Request $request, UserInterface $user, Post $post)
{
// 1) Build the form
$comment = new Comment();
$comment->setPost($post); //where $post is instance of App\Entity\Post
$form = $this->createForm(CommentType::class, $comment);
// 2) Handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
// 3) Save the comment!
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($comment);
$entityManager->flush();
}
return $this->redirectToRoute('homepage');
}
CommentType
public function buildForm(FormBuilderInterface $builder, array $options)
{
//don't need to set the $post here
$builder->add('content', TextareaType::class, [
'constraints' => [
new Assert\NotBlank(['message' => 'Your comment cannot be blank.']),
new Assert\Length([
'min' => 10,
'minMessage' => 'Your comment must be at least {{ limit }} characters long.',
]),
],
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Comment::class
//don't need the default here either
]);
}
Comment Entity
class Comment
{
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Post")
*/
private $post;
//other vars
public function setPost(\App\Entity\Post $post): void
{
$this->post = $post;
}
public function getPost(): \App\Entity\Post
{
return $this->post;
}
//other functions
}
This code works for me:
CommentController.php
As suggested by flint above, you just need to pass the actual Post entity, not just the id. Then if you have this error "Unable to guess how to get a Doctrine instance from the request information for parameter "post" this is because you need to add the post slug in the path of the new_comment route. The ParamConverter is called implicitly and it need this slug {post} with the same name as the name you used for the post parameter in the function.
/**
* #param Request $request
* #return \Symfony\Component\HttpFoundation\RedirectResponse
* #Route("/comment/new/{post}", name="new_comment")
*/
public function new(Request $request, Post $post)
{
$comment = new Comment();
$comment->setPost($post); //where $post is instance of App\Entity\Post
$form = $this->createForm(CommentType::class, $comment);
// 2) Handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
// 3) Save the comment!
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($comment);
$entityManager->flush();
}
return $this->redirectToRoute('homepage');
}
PostController.php
/**
* #Route("/post/{id}", name="get_post")
*/
public function getPostAction(Post $post)
{
// Generate the comment form.
$comment = new Comment();
$form = $this->createForm(CommentType::class, $comment, [
'action' => $this->generateUrl('new_comment', ['post' => $post->getId()]),
]);
return $this->render('listeArticles.html.twig', [
'form' => $form->createView()
]);
}
CommentType.php
class CommentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
//don't need to set the $post here
$builder
->add('content', TextareaType::class, [
'constraints' => [
new Assert\NotBlank(['message' => 'Your comment cannot be blank.']),
new Assert\Length([
'min' => 10,
'minMessage' => 'Your comment must be at least {{ limit }} characters long.',
]),
],
])
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Comment::class
]);
}
}
With this you don't need to remove the Doctrine relationship between the two tables and manually set an ID.
Dont put in to form field,
for exampled
public function new(Request $request, UserInterface $user)
{
// 1) Build the form
$comment = new Comment();
$form = $this->createForm(CommentType::class, $comment);
// 2) Handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
comment->setPostId($post_id)
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($comment);
$entityManager->flush();
}
return $this->redirectToRoute('homepage');
}
The error message says it all:
Expected argument of type "App\Entity\Post or null", "string" given.
If you go to your comment Entity (App\Entity\Comment) you'll see that your class refers to the parent post as a Post Class (App\Entity\Post) and not as a "post_id".
It is the ORM (doctrine in this case) who does the link in your physical database and your Entity classes and add a post_id field in your table.
This is the what ORM (Object Relational Model) is for. You should no more consider Post and Comment as Sql tables but as Classes (OOP).
Thus is I want to add a comment related to someParent I should do something like:
$comment = new Comment();
$comment->setPost($post);
Where $post is an instance of the class Post.
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.
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 have 3 Entities: User , Report and ReportCategory.
A User can put Reports in ONE ReportCategory. In the User-Entity there is a list, which ReportCategories are allowed for the user. This works all fine - I made it with a connectionTable which has the userID and the reportCategoryId.
Now I make an Array in Controller to get all ReportCategories of the current logged in User:
public function newAction()
{
$entity = new Report();
$form = $this->createCreateForm($entity);
$userId = $this->get('security.context')->getToken()->getUser()->getId();
$user = $this->getDoctrine()->getRepository('MyBundle:User')->find($userId);
$userReportCategories = array();
foreach($user->getReportCategories() as $reportCategory)
{
$userReportCategories[] = $reportCategory->getId();
}
return array(
'entity' => $entity,
'form' => $form->createView(),
'userReportCategories' => $userReportCategories
);
}
How can I set only these Values to my twig template field? When I make an own field it is not managed form Doctrine!
{{ form_row(form.reportCategory, {'attr': {'class': 'form-control'}, 'label': 'Category'}) }}
THANKS FOR ANY HELP!!!
UPDATE:
My ReportType looks like this:
class ReportType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('reportCategory')
->add('creationDate', 'date', array(
'data' => new \DateTime()
))
->add('headline')
->add('text')
->add('user')
;
}
....
Create a form class and add an event listener so that the form is aware of the user. The following is an adaptation of How to dynamically Generate Forms Based on user Data in the Symfony docs. [It is not guaranteed to accurately capture your needs].
form class
use Symfony\Component\Security\Core\SecurityContext;
use Doctrine\ORM\EntityRepository;
// ...
class ReportFormType extends AbstractType
{
private $securityContext;
public function __construct(SecurityContext $securityContext)
{
$this->securityContext = $securityContext;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// grab the user, do a quick sanity check that one exists
$user = $this->securityContext->getToken()->getUser();
if (!$user) {
throw new \LogicException(
'The ReportFormType cannot be used without an authenticated user!'
);
}
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($user) {
$form = $event->getForm();
$formOptions = array(
'class' => 'Acme\DemoBundle\Entity\ReportCategory',
'property' => 'category',
'query_builder' => function (EntityRepository $er) use ($user) {
// build a custom query
// return $er->createQueryBuilder('c')
->select('category')
->where('user = $user);
},
);
// create the field, this is similar the $builder->add()
// field name, field type, data, options
$form->add('userReportCategories', 'entity', $formOptions);
}
);
}
// ...
}
new action
public function newAction()
{
$entity = new Report();
$form = $this->createForm(new ReportFormType(), $entity);
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}