Symfony2: Can't create new entity with form - symfony

I am having problem with creating new Collection entity with Form.
I want to create new Collection entity with form and then to be redirected to collections page with 'collection_user_collections' route, and be able to see new collection in user's collections list. But instead when I press submit button on form, I get following error:
No route found for "POST /profile/": Method Not Allowed (Allow: GET, HEAD)
Below is my code:
class Collection{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
private $name;
private $url;
private $type;
const STATUS_PRIVATE = 0;
const STATUS_PUBLIC = 1;
/**
* #ORM\ManyToOne(targetEntity="MyMini\UserBundle\Entity\User", inversedBy="collections")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
private $date_created;
private $date_modified;
/* getters and setters are here*/
}
I am using CollectionType to build form:
class CollectionType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name','text')
->add('type', 'choice', array('choices' => array(
Collection::STATUS_PRIVATE => 'Private',
Collection::STATUS_PUBLIC => 'Public',
)))
->add('save', 'submit', array('label' => 'Create Collection'))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyMini\CollectionBundle\Entity\Collection'
));
}
public function getName()
{
return 'mymini_collectionbundle_collection';
}
}
This is createAction, here I tried to insert current user's username and date when entity was created. I am using FOSUserBundle to manage app users:
/**
* #Route("/create-collection/", name="collection_create_collection")
* #Template()
*/
public function createAction(Request $request)
{
$collection = new Collection();
$user = $this->get('security.token_storage')->getToken()->getUser();
$username = $user->getUsername();
$form = $this->createForm(new CollectionType(), $collection);
$form->handleRequest($request);
if ($form->isValid() && $form->isSubmitted()) {
$em = $this->getDoctrine()->getManager();
$collection->setUser($user);
$collection->setDateCreated(new \DateTime());
$em->persist($collection);
$em->flush();
return $this->redirectToRoute('collection_user_collections', array('username' => $username));
}
return array('collection'=>$collection, 'form' => $form->createView());
}
Twig for form:
<div class="collection-create">
<h3 id="create-collection">Create a collection</h3>
<a class="close-reveal-modal" aria-label="Close">×</a>
{{ form(form) }}
</div>

The exception you're receiving is expected. You are calling the createForm method without passing all necessary arguments. The right way to create a form is:
$this->createForm(
new CollectionType(),
$collection,
array(
'action' => $this->generateUrl('collection_create_collection') ),
'method' => 'PUT', // or 'POST'
)
);

Related

When to call validator in Symfony forms?

I dont know when to call validation in this situation ? I made some constraints in Form/Model/User.php class for propertys, and don't know where to call it and how.And how to display errors on the same page.
What is best practice to do this right ?
public function userRegistrationAction(Request $request) {
$formUser = new FormUser();
$form = $this->createForm(UserType::class, $formUser);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$userEntity = new EntityUser();
$name = $form['name']->getData();
$surname = $form['surname']->getData();
$email = $form['email']->getData();
$password = $this->get('security.password_encoder')
->encodePassword($userEntity, $form['password']->getData());
$now = new\DateTime('now');
$userEntity->setName($name);
$userEntity->setSurname($surname);
$userEntity->setEmail($email);
$userEntity->setPassword($password);
$userEntity->setCreated($now);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($userEntity);
$entityManager->flush();
$request->getSession()
->getFlashBag()
->add('success', '- Success! ');
return $this->render('AppBundle:Welcome:homepage.html.twig', array(
'name' => $name,
'lastName' => $surname,
));
}
Did you created a formUser entity to fill a form then fill a user entity ? This make a lot of redundancies.
Your controller should looks like that:
<?php
public function userRegistrationAction(Request $request)
{
$userEntity = new EntityUser();
/*
You can directly provide your user entity to your form
There is no need to create a specific entity fir the form
*/
$form = $this->createForm(UserType::class, $userEntity);
$form->handleRequest($request);
/*
method isValid checks the form has been submitted so there
is no need to call isSubmitted by yourself
*/
if ($form->isValid()) {
/*
The password encryption part could be improved by
doing it outside of the controller
*/
$password = $this->get('security.password_encoder')
->encodePassword($userEntity, $userEntity->getPassword())
;
$userEntity->setPassword($password);
$entityManager = $this->getDoctrine()->getManager();
/*
By providing your user at your form at the begining all it
remains to do is persisting it and flush the manager
to insert it into your database.
*/
$entityManager->persist($userEntity);
$entityManager->flush();
$this->addFlash('success', '- Success! ');
return $this->render('AppBundle:Welcome:homepage.html.twig', array(
'name' => $name,
'lastName' => $surname,
));
}
return $this->render('AppBundle:User:userRegistration.html.twig', array(
'form' => $form->createView()
));
}
Now your form should look like that
<?php
class UserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('surname')
->add('email')
->add('password', PasswordType::class)
->add('submit', SubmitType::class)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\EntityUser',
));
}
/**
* #return string
*/
public function getName()
{
return 'user_type';
}
}
Your entity EntityUser
<?php
/**
* #ORM\HasLifecycleCallbacks()
*/
class EntityUser extends Entity
{
/**
* #ORM\PrePersist
*/
public function setCreatedAtValue()
{
/*
By using the PrePersist annotation you will automatically set
the createdAt property
*/
$this->createdAt = new \DateTime();
}
}
And finally your twig template
{# form_row is a shortcut to render at once
form_errors(form.field)
form_label(form.field)
form_widget(form.field)
so if you have errors on some of your fields there will be displayed #}
{{ form_start(form) }}
{{ form_row(form.name) }}
{{ form_row(form.surname }}
{{ form_row(form.email) }}
{{ form_row(form.password) }}
{{ form_row(form.submit) }}
{{ form_end(form) }}
OK, this was the problem: 'validation_groups' => ['registration'], i set this group for only one field, now i delete this and working nice, no need for callig validator after isValid.
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => ['registration'], //delete this
'method' => 'post',
'data_class' => User::class,
'csrf_protection' => true,
'cascade_validation' => true
));
}

How to load data in ChoiceType choices options from the database

I want to build a form with ChoiceType and the option values/choices are based on database table (with records already).
When the form displayed, the list of religions will be available at the dropdown list/combo box.
Example :
$builder->add('name', ChoiceType::class, array(
'choices' => $religions //List of religions
));
So far here are my codes:
class Religion
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=50)
*/
protected $name;
/*** getter/setter ... ***/
}
/Form/ReligionType
class ReligionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', ChoiceType::class, array(
'choices' => ____________
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Religion'
));
}
public function getName()
{
return 'app_bundle_religion_type';
}
}
/Controller
/**
* #Route("/religion/select", name="religion_select")
*
*/
public function selectAction()
{
$em = $this->getDoctrine()->getManager();
$religions = $em->getRepository('AppBundle:Religion')->findAll();
$form = $this->createForm(ReligionType::class, ________);
return $this->render(
'religion/index.html.twig', array(
'form' => $form->createView()
)
);
}
I don't really know what to write so i leave it as __________ and what are the missing codes.
/ReligionRepository
class ReligionRepository extends EntityRepository
{
public function findAll()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AppBundle:Religion p ORDER BY p.name ASC'
)
->getResult();
}
}
/Twig File
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(form) }}
{{ form_widget(form) }}
<button type="submit">Save</button>
{{ form_end(form) }}
{% endblock %}
as suggested by Rvanlaak, here's the solution.
//ReligionType
$builder->add('religions', EntityType::class, array(
'class' => 'AppBundle\Entity\Religion',
'choice_label' => 'name'
));
//Controller
public function newAction(Request $request)
{
$religion = new Religion();
$form = $this->createForm(ReligionType::class, $religion);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($religion);
$em->flush();
//return $this->redirectToRoute('homepage');
}
return $this->render(
'religion/new.html.twig',
array(
'form' => $form->createView()
)
);
}
I did like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('currency', null, [
'expanded' => true,
'multiple' => true,
'label' => 'Currency'
])
In my entity like this:
protected $currency;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->Currency = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #param \MyBundle\Entity\Currency $Currency
*
* #return $this
*/
public function addCurrency(\MyBundle\Entity\Currency $Currency)
{
$this->Currency[] = $Currency;
return $this;
}
/**
* Remove Currency
*
* #param \MyBundle\Entity\Currency $Currency
*/
public function removeCurrency(\MyBundle\Entity\Currency $Currency)
{
$this->Currency->removeElement($Currency);
}
/**
* Get Currency
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCurrency()
{
return $this->Currency;
}
Both posted solutions didn't work for me because the ChoiceType type was removed from it. Here is a solution with the ChoiceType still existing:
I wanted to generate a list with types of absents users can choose from. These types are created by a user in the system settings. I first load them from db, then create one array (for loop) with where the ["name"] will be the name the user sees. The id is the value that the html select field will have and return when selected and saved.
What I added and could not find anywhere was the extra option to pass an option in the createForm function. This enables you to send data to the form basicly.
$systemAbsents = $this->getDoctrine()->getRepository(SystemAbsent::class)->getAllNonDeletedSystemAbsents();
$choices = [];
// Add each choice to the list. The id's have to match correctly so the html choicetype will return the chosen id that then will be saved in the db.
for ($i = 0; $i < count($systemAbsents); $i++) {
$choices += [$systemAbsents[$i]["name"] => $systemAbsents[$i]["id"]];
}
$absent = new Absent();
// Create form and pass the choices to later connect them with the ChoiceType field.
$form = $this->createForm(AbsentType::class, $absent, ['choices' => $choices]);
$form->handleRequest($request);
Then in the form, you can use the data and pass it to the 'choices' option.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->choices = $options['choices'];
$builder
->add('choices', ChoiceType::class, [
'choices' => $this->choices,
'label_attr' => [
'class' => 'bold',
],
'attr' => [
'class' => 'input-margin select-field w-100',
],
'mapped' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'choices' => [],
]);
}
The array I create in the for loop looks like this:
[
"absent-type-1" => 10
"absent-type-2" => 11
"absent-type-3" => 12
"absent-type-4" => 13
"absent-type-5" => 14
"absent-type-6" => 15
"absent-type-7" => 16
"absent-type-8" => 17
]
The ID starts at 10 because I deleted some while testing.
If you by default want to show a selected value in the ChoiceType field you can do setData after you create the form. Like this:
// Create form
$form = $this->createForm(AbsentType::class, $absent, ['choices' => $choices]);
// Add this line to set a default. But make sure you pass the value equal to the html `value` attribute.
$form->get('system_absents')->setData($absent->getSystemAbsentID());
$form->handleRequest($request);
There is a possiblity you get an Array to string conversion error. I dont know how exactly this works but i've once used this solution before.

Symfony 2: Dynamic Form Event returns InvalidArgumentException only when editing

I got an entity called 'Activity', that defines a relation between 2 more entities, 'Service' and 'Location'.
Both 'Service' and 'Location', use another entity called 'Allocation', to define what services can be used in a concrete location.
When I create a new Activity, after selecting a service I want the location choice field update with the values defined by allocation.
I have followed symfony documentation to create this 'location' dependent choice field in the form.
Dynamic Form Modification
All works great on create/new form, but when i try to edit service field value in an already created Activity, location field does not update and the symfony profiler shows me the following message:
Uncaught PHP Exception Symfony\Component\PropertyAccess\Exception\InvalidArgumentException: "Expected argument of type "AppBundle\Entity\Location", "NULL" given" at F:\xampp\htdocs\gcd\vendor\symfony\symfony\src\Symfony\Component\PropertyAccess\PropertyAccessor.php line 253 Context: { "exception": "Object(Symfony\Component\PropertyAccess\Exception\InvalidArgumentException)" }
This is a section of my Activity Entity
/**
* Activity
*
* #ORM\Table(name="activity")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ActivityRepository")
*/
class Activity
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Service
*
* #ORM\ManyToOne(targetEntity="Service", fetch="EAGER")
* #ORM\JoinColumn(name="service_id", referencedColumnName="id", nullable=false)
*/
private $service;
/**
* #var Location
*
* #ORM\ManyToOne(targetEntity="Location", fetch="EAGER")
* #ORM\JoinColumn(name="location_id", referencedColumnName="id", nullable=false)
*/
private $location;
My Controller.
/**
* Creates a new Activity entity.
*
* #Route("/new", name="core_admin_activity_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$activity = new Activity();
$form = $this->createForm('AppBundle\Form\ActivityType', $activity);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$locationAvailable = $this->isLocationAvailable($activity);
$activityOverlap = $this->hasOverlap($activity);
if($locationAvailable && !$activityOverlap){
$em = $this->getDoctrine()->getManager();
$em->persist($activity);
$em->flush();
return $this->redirectToRoute('core_admin_activity_show', array('id' => $activity->getId()));
}
}
return $this->render('activity/new.html.twig', array(
'activity' => $activity,
'form' => $form->createView(),
));
}
/**
* Displays a form to edit an existing Activity entity.
*
* #Route("/{id}/edit", name="core_admin_activity_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Activity $activity)
{
$deleteForm = $this->createDeleteForm($activity);
$editForm = $this->createForm('AppBundle\Form\ActivityType', $activity);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$locationAvailable = $this->isLocationAvailable($activity);
$activityOverlap = $this->hasOverlap($activity);
if($locationAvailable && !$activityOverlap){
$em = $this->getDoctrine()->getManager();
$em->persist($activity);
$em->flush();
return $this->redirectToRoute('core_admin_activity_show', array('id' => $activity->getId()));
}
}
return $this->render('activity/edit.html.twig', array(
'activity' => $activity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
My FormType
class ActivityType extends AbstractType
{
private $em;
public function __construct(EntityManager $entityManager)
{
$this->em = $entityManager;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('service', EntityType::class, array(
'class' => 'AppBundle:Service',
'placeholder' => 'elige servicio',
))
->add('location', EntityType::class, array(
'class' => 'AppBundle:Location',
'choices' => array(),
))
->add('name')
->add('virtual')
->add('customerSeats')
->add('customerVacants')
->add('employeeSeats')
->add('firstDate', 'date')
->add('lastDate', 'date')
->add('weekday')
->add('beginTime', 'time')
->add('endTime', 'time')
->add('admissionType')
->add('status');
$formModifier = function (FormInterface $form, Service $service = null) {
$locations = null === $service ? array() : $this->em->getRepository('AppBundle:Allocation')->findLocationsByService($service);
$form->add('location', EntityType::class, array(
'class' => 'AppBundle:Location',
'choices' => $locations,
));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
$formModifier($event->getForm(), $data->getService());
}
);
$builder->get('service')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$service = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions!
$formModifier($event->getForm()->getParent(), $service);
}
);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Activity'
));
}
}
javaScript function
<script>
var $service = $('#activity_service');
// When sport gets selected ...
$service.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected service value.
var data = {};
data[$service.attr('name')] = $service.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
// Replace current position field ...
$('#activity_location').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#activity_location')
);
}
});
});
</script>
Any help will be great, thanks.
I had a similar problem, I found a solution : Symfony - dynamic drop down lists not working only when editing
I had also faced similar issue and on tracing found that it is the EntityType class on other dropdown that is causing the problem while editing the form
The solution is to submit the full form via ajax instead of only one field like in the case of new form.
So change
var data = {};
data[$service.attr('name')] = $service.val();
To
var data = $form.serializeArray()
That should fix the issue.

How to persist an entity which contains properties displayed in a form as select list

I'm a newby on symfony.
I'm working on a project using symfony 2.7.6.
I have an object/entity "Trajet" which contains 2 properties(which are source of my headaches!!).
Theses 2 properties are VilleDepart and VilleArrivee (integers linked to my database with doctrine (ORM annotation in the object class))
In my form i want them to be displayed as a dropdown list. So i used a formbuilder with the two entity field type.
But when i try to persist the "Trajet" object in the controller (using entity manager), i get this an sql error (because it consider the two properties VilleDepart and VilleArrivee as entity instead of integer)...
I clearly see where the problem is... the sql query is
INSERT INTO trajet (traj_villedepart , traj_villearrivee) VALUES ({}, {})
Instead of
INSERT INTO trajet (traj_villedepart , traj_villearrivee) VALUES (1, 2)
For example
Here his my class trajet
class Trajet {
/**
* #ORM\Id #ORM\Column(name="idtrajet", type="integer")
* #ORM\GeneratedValue
*/
private $ID;
/**
* #ORM\Column(name="traj_idvilledepart", type="integer")
*/
private $VilleDepart;
/**
* #ORM\Column(name="traj_idvillearrivee", type="integer")
*/
private $VilleArrivee;
//Getters and setters....
}
Here is my abstract type of Trajet for the form
class TrajetType extends AbstractType
{
private $manager;
public function __construct(ObjectManager $manager)
{
$this->manager = $manager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('DescriptionVoiture', 'textarea')
->add('VilleDepart', 'entity', array(
'class' => 'MyBundle:Ville',
'property' => 'Nom',
'em' => 'pgsql',
'query_builder' => function(VilleRepository $er){
return $er->getVillesOrderByOrdre();
},
))
->add('VilleArrivee', 'entity', array(
'class' => 'MyBundle:Ville',
'property' => 'Nom',
'em' => 'pgsql',
'query_builder' => function(VilleRepository $er){
return $er->getVillesOrderByOrdre();
},
))
}
public function getName()
{
return 'trajet';
}
}
And finally my controller
class TrajetController extends Controller {
public function AddEditTrajetAction($idTrajet = null) {
$manager = $this->getDoctrine()->getManager('pgsql');
$trajet = new Trajet();
//Initialize the object $trajet with the get ID....
$form = $this->createForm(new TrajetType($manager), $trajet);
$request = $this->getRequest();
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
$manager->persist($trajet);
$manager->flush();
//Return to the view...
}
}
}
}
Please help! I become crazy!! thanks
I finally found the solution: Make an unidirectional oneToone relationship between Trajet and Ville I Modified the class Trajet like that
class Trajet {
/**
*#ORM\OneToOne(targetEntity="xxx\Entity\Ville")
*#ORM\JoinColumn(name="traj_idvilledepart", ReferencedColumnName="idville", nullable=false)
*/
private $VilleDepart;
//The same for VilleArrivee...
//Getters and setters....
}
And then in the controlleur
$manager->persist($trajet);
$manager->flush();
Will work easily

Persisting self referencing entity not works

I create an entity with self referencing. My entity look like this:
class Question
{
/**
* #ORM\OneToMany(targetEntity="Question", mappedBy="parent")
**/
private $children;
/**
* #ORM\ManyToOne(targetEntity="Question", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
**/
private $parent;
}
And i create a form to edit a Question. With this form i can add many childs to a Question. After i post this form i will save the childs for a parent Object. But the persisting of childs for a parent fails, nothing happens in the database.
public function manageDependencyAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$question = $em->getRepository('AppMyBundle:Question')->find($id);
if (!$question) {
$this->get('session')->getFlashBag()->add('danger', $this->get('translator')->trans('objectNotFound'));
return $this->redirect($this->generateUrl('app_question_list'));
}
$form = $this->createForm($this->get('form.type.question'), $question, array())->add('save', 'submit', array('label' => 'save', 'translation_domain' => 'messages', 'attr' => array('class' => 'btn btn-primary')));
$form->handleRequest($request);
if ($form->isValid()) {
// dump($question->getChildren()); // This is not empty. In this array are the selected childs.
$em->persist($question);
$em->flush();
}
}
change your entity methods:
public function addChild(Question $children)
{
$this->children[] = $children;
$children->setParent($this);
return $this;
}

Resources