Symfony3 customize label of EntityType choices - symfony

I search a solution to customize the label of choice of EntityType.
Entities
Post
class Post
{
// ...
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Item", cascade={"persist"})
*/
private $items;
// ...
}
Item
class Item
{
// ...
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=127)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="image", type="string", length=255, nullable=true)
*/
private $image;
// ...
public function __toString(){
return $this->title;
}
}
Form
class PostType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('items', EntityType::class, array(
'class' => 'AppBundle\Entity\Item',
'multiple' => true,
'expanded' => true,
))
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Post'
));
}
}
Result
I know how to modify the DOM to get :
<ul>
<li>
<input type="checkbox" id="post_items_1" name="post[items][]" value="1">
<label for="post_items_1">Item 1</label>
</li>
<li>
<input type="checkbox" id="post_items_2" name="post[items][]" value="2">
<label for="post_items_2">Item 2</label>
</li>
<!-- ... -->
</ul>
But I would like get other informations from the Item choices (like property image) :
<ul>
<li>
<input type="checkbox" id="post_items_1" name="post[items][]" value="1">
<label for="post_items_1">
Item 1
<img src="/uploads/item/lorem.jpg" alt="" /> <!-- path store into item #1 -->
</label>
</li>
<li>
<input type="checkbox" id="post_items_2" name="post[items][]" value="2">
<label for="post_items_2">
Item 2
<img src="/uploads/item/ipsum.jpg" alt="" /> <!-- path store into item #2 -->
</label>
</li>
<!-- ... -->
</ul>
Does anyone have a solution?

Setting a choice_label is what you're looking for:
$builder->add('users', EntityType::class, array(
'class' => 'AppBundle:User',
'choice_label' => 'username',
));
Source: http://symfony.com/doc/current/reference/forms/types/entity.html
If you want to use images in your label, you can customize your form template. You can read about it here:
http://symfony.com/doc/current/cookbook/form/form_customization.html#cookbook-form-theming-methods
http://symfony.com/doc/current/book/forms.html#form-theming

Related

Symfony 4 save button to submit request keep reloading the current page

Actually there's no request sent as if the function isnt even executed
it already shows in the logs that it uses POST/SponsorNew and doesn't load "SponsorListPage"
after submitting save it in the database
besides i applied the same code on another entity from the same project and it worked
//Add method
/**
* #Route("/sponsorNew",name="newSponsorPage")
*/
public function newSponsor(Request $req):Response{
//1.Create form view
$sponsor= new Sponsor();
//1.b prepare the form
$form= $this->createForm(SponsorType::class, $sponsor);
//2. Handel http request sent by the user
$form=$form->handleRequest($req);
//2.b check the form
if($form->isSubmitted() && $form->isValid()){
//3.Persist data
$em=$this->getDoctrine()->getManager();
$em->persist($sponsor);
$em->flush();
return $this->redirectToRoute("SponsorListPage");
}
//1.c render the form
return $this->render('sponsor/new.html.twig',[
'f'=>$form->createView()
]);
}
and here is the Form
class SponsorType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('nom')
->add('email')
->add('numContact')
->add('type')
->add('sponsor_save', SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Sponsor::class,
]);
}
}
here is the template to fill the blanks
<div class="container">
<h1>Add new Sponsor</h1>
{{ form_start(f) }}
<div class="form-group">
<label>
Name of the Sponsor
</label>
{{ form_widget(f.nom,{'attr':{'class':'form-control'}}) }}
</div>
<div class="form-group">
<label>
Email of Sponsor
</label>
{{ form_widget(f.email,{'attr':{'class':'form-control'}}) }}
</div>
<div class="form-group">
<label>
Contact Number
</label>
{{ form_widget(f.numContact,{'attr':{'class':'form-control'}}) }}
</div>
<div class="form-group">
<label>
Type
</label>
{{ form_widget(f.type,{'attr':{'class':'form-control'}}) }}
</div>
{{ form_widget(f.sponsor_save,{'attr':{'class':'btn btn-sm btn-success'}}) }}
{{ form_end(f) }}
</div>

Symfony - Expected value of type Entity for association field got “string” instead

I have a UserEntity containing users email addresses and their password. Then a UserInstanceEntity that contains the users phone numbers as well as a ManyToOne relation on a PreferredContactEntity that allows the user to choose the contact method he prefers.
I wish when a user changes his profile that he can also modify his phone numbers and preferred method of contact. So in my controller I inserted UserType and UserInstanceType.
When I send the form, I get the error:
Expected value of type "App\Entity\PreferredContact" for association
field "App\Entity\UserInstance#$preferredContact", got "string"
instead.
UserEntity :
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use App\Validator\Constraints as AssertPerso;
use DateTime;
use DateTimeInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\Table(name="app_user")
* #ORM\HasLifecycleCallbacks()
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
* #AssertPerso\Email
*/
private $email;
/**
* #ORM\Column(type="array")
*/
private $roles = [];
/**
* #var string plain password
*/
private $plainPassword;
/**
* #ORM\Column(type="boolean")
*/
private $enabled;
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
...
UserInstanceEntity :
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserInstanceRepository")
* #ORM\Table(name="app_user_instance")
*/
class UserInstance
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $contactNumber;
/**
* #var string
*
* #ORM\Column(type="string", nullable=true)
*/
private $directContactNumber;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\PreferredContact")
* #ORM\JoinColumn(nullable=false)
*/
private $preferredContact;
PreferredContactEntity :
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\PreferredContactRepository")
* #ORM\Table(name="app_user_preferred_contact")
*/
class PreferredContact
{
/**
* #var int
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", length=180, unique=true)
*/
private $contact;
Table PreferredContact :
id contact
1 Mail
2 Phone
3 Webex Teams
UserController :
public function editProfil(Request $request): Response
{
$user = $this->getUser();
$userInstance = new UserInstance();
$form = $this->createForm(EditProfilType::class, $user);
$formUserInstance = $this->createForm(UserInstanceType::class, $userInstance);
$form->handleRequest($request);
$formUserInstance->handleRequest($request);
if ($form->isSubmitted() && $form->isValid() && $formUserInstance->isValid()) {
$this->getDoctrine()->getManager()->flush();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($userInstance);
$entityManager->flush();
$this->addFlash('success', 'Informations mises à jour avec succès !');
return $this->redirectToRoute('user_editprofil');
}
return $this->render($this->ticketingTemplates['edit_profil'], [
'user' => $user,
'form' => $form->createView(),
'formUserInstance' => $formUserInstance->createView(),
]);
}
UserType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', null, array(
'label' => 'Email',
'required' => true)
)
->add('plainPassword',PasswordType::class, array(
'label' => 'Mot de passe',
'required' => true,
'constraints' => [
new Length([
'min' => 6,
'minMessage' => '{{ limit }} caractères minimum',
'max' => 4096,
]),
],
)
)
->add('roles', RolesType::class);
UserInstanceType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('contactNumber', null, array(
'label' => 'Numéro de téléphone portable',
'required' => true,
))
->add('directContactNumber', null, array(
'label' => 'Numéro de ligne direct'), [
'required' => false,
'help' => 'Facultatif',
])
->add('preferredContact', EntityType::class, [
'class' => PreferredContact::class,
'label' => 'Méthode de contact préférée',
]);
editprofil.html.twig :
<form method="post" >
<fieldset>
<legend><i class="fa fa-lock" aria-hidden="true"></i> {{ 'Votre compte' }}</legend>
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_start(formUserInstance) }}
{{ form_widget(formUserInstance) }}
{{ form_end(formUserInstance) }}
<button type="submit" class="btn btn-primary">
<i class="fa fa-save" aria-hidden="true"></i> {{ 'Enregistrer' }}
</button>
<a href="{{ path('user_change_password') }}" class="btn btn-danger">
<i class="fa fa-lock" aria-hidden="true"></i> {{ 'Modifier le mot de passe' }}
</a>
{{ form_end(form) }}
</fieldset>
</form>
Do you know where this error comes from? Thank you
Edit :
EditProfilType :
class EditProfilType extends AbstractType
{
private $securityChecker;
private $token;
public function __construct(AuthorizationCheckerInterface $securityChecker, TokenStorageInterface $token)
{
$this->securityChecker = $securityChecker;
$this->token = $token;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', null, array(
'label' => 'Email ',
'required' => true
)
)
->add('firstName', null, array(
'label' => 'Prénom',
'required' => true)
)
->add('lastName', null, array(
'label' => 'Nom',
'required' => true)
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'data_class' => User::class,
]
);
}

Symfony2 Doctrine ORM: id field remains NULL

I am using CollectionType Field in my Symfony project, to be able to generate as many similar form items as user needs. The problem I have appears while form is being submitted - it passes all the values directly to db, but the foreign key id (household_app_id) remains null, even though relations seem to be correct.
What can I do to prevent this bug? And why is this happening?
My project code is below:
Both Entities:
<?php //HouseholdApp.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* HouseholdApp
*
* #ORM\Table(name="household_application")
* #ORM\Entity(repositoryClass="AppBundle\Repository\HouseholdAppRepository")
* #Vich\Uploadable
*
*/
class HouseholdApp
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* One Application has One Household.
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Household", inversedBy="householdApp")
* #ORM\JoinColumn(name="household_id", referencedColumnName="id")
*/
private $household;
/**
* #var File
*
* #Vich\UploadableField(mapping="union_register", fileNameProperty="unionRegisterName")
*/
private $unionRegister;
/**
* #var File
*
* #Vich\UploadableField(mapping="apt_owners_decision", fileNameProperty="aptOwnersDecisionName")
*/
private $aptOwnersDecision;
/**
* #var File
*
* #Vich\UploadableField(mapping="mulaptinventory", fileNameProperty="multiAptInventoryName")
*/
private $multiAptInventory;
/**
* #var File
*
* #Vich\UploadableField(mapping="buildtechsurvey", fileNameProperty="buildingTechnicalSurveyName")
*/
private $buildingTechnicalSurvey;
/**
* #var File
*
* #Vich\UploadableField(mapping="defectact", fileNameProperty="defectActName")
*/
private $defectAct;
/**
* #var File
*
* #Vich\UploadableField(mapping="activitycost", fileNameProperty="activityCostName")
*/
private $activityCost;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\DefectAct", mappedBy="householdApp", cascade={"persist"}, orphanRemoval=true)
*/
protected $actOfDefects;
/**
* Set unionRegister
*
* #param File|UploadedFile $unionRegister
*
* #return HouseholdApp
*/
public function setUnionRegister(File $unionRegister = null)
{
$this->unionRegister = $unionRegister;
if ($unionRegister) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->uUpdatedAt = new \DateTime('now');
}
return $this;
}
/**
* Get unionRegister
*
* #return File|UploadedFile
*/
public function getUnionRegister()
{
return $this->unionRegister;
}
/**
* Set aptOwnersDecision
*
* #param File|UploadedFile $aptOwnersDecision
*
* #return HouseholdApp
*/
public function setAptOwnersDecision(File $aptOwnersDecision = null)
{
$this->aptOwnersDecision = $aptOwnersDecision;
if ($aptOwnersDecision) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->aUpdatedAt = new \DateTime('now');
}
return $this;
}
/**
* Get aptOwnersDecision
*
* #return File|UploadedFile
*/
public function getAptOwnersDecision()
{
return $this->aptOwnersDecision;
}
/**
* Set multiAptInventory
*
* #param File|UploadedFile $multiAptInventory
*
* #return HouseholdApp
*/
public function setMultiAptInventory(File $multiAptInventory = null)
{
$this->multiAptInventory = $multiAptInventory;
if ($multiAptInventory) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->mUpdatedAt = new \DateTime('now');
}
return $this;
}
/**
* Get multiAptInventory
*
* #return File|UploadedFile
*/
public function getMultiAptInventory()
{
return $this->multiAptInventory;
}
/**
* Set buildingTechnicalSurvey
*
* #param File|UploadedFile $buildingTechnicalSurvey
*
* #return HouseholdApp
*/
public function setBuildingTechnicalSurvey(File $buildingTechnicalSurvey = null)
{
$this->buildingTechnicalSurvey = $buildingTechnicalSurvey;
if ($buildingTechnicalSurvey) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->bUpdatedAt = new \DateTime('now');
}
return $this;
}
/**
* Get buildingTechnicalSurvey
*
* #return File|UploadedFile
*/
public function getBuildingTechnicalSurvey()
{
return $this->buildingTechnicalSurvey;
}
/**
* Set defectAct
*
* #param File|UploadedFile $defectAct
*
* #return HouseholdApp
*/
public function setDefectAct(File $defectAct = null)
{
$this->defectAct = $defectAct;
if ($defectAct) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->dUpdatedAt = new \DateTime('now');
}
return $this;
}
/**
* Get defectAct
*
* #return File|UploadedFile
*/
public function getDefectAct()
{
return $this->defectAct;
}
/**
* Set activityCost
*
* #param File|UploadedFile $activityCost
*
* #return HouseholdApp
*/
public function setActivityCost(File $activityCost = null)
{
$this->activityCost = $activityCost;
if ($activityCost) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->acUpdatedAt = new \DateTime('now');
}
return $this;
}
/**
* Get activityCost
*
* #return File|UploadedFile
*/
public function getActivityCost()
{
return $this->activityCost;
}
}
<?php //DefectAct.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* DefectAct
*
* #ORM\Table(name="defect_act")
* #ORM\Entity(repositoryClass="AppBundle\Repository\DefectActRepository")
*
*/
class DefectAct
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $defectWork;
/**
* #var string
*
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $defectDescription;
/**
* #var string
*
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $preventionDeadline;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\HouseholdApp", inversedBy="actOfDefects")
*/
private $householdApp;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set defectWork
*
* #param string $defectWork
*
* #return DefectAct
*/
public function setDefectWork($defectWork)
{
$this->defectWork = $defectWork;
return $this;
}
/**
* Get defectWork
*
* #return string
*/
public function getDefectWork()
{
return $this->defectWork;
}
/**
* Set defectDescription
*
* #param string $defectDescription
*
* #return DefectAct
*/
public function setDefectDescription($defectDescription)
{
$this->defectDescription = $defectDescription;
return $this;
}
/**
* Get defectDescription
*
* #return string
*/
public function getDefectDescription()
{
return $this->defectDescription;
}
/**
* Set preventionDeadline
*
* #param string $preventionDeadline
*
* #return DefectAct
*/
public function setPreventionDeadline($preventionDeadline)
{
$this->preventionDeadline = $preventionDeadline;
return $this;
}
/**
* Get preventionDeadline
*
* #return string
*/
public function getPreventionDeadline()
{
return $this->preventionDeadline;
}
/**
* Set householdApp
*
* #param \AppBundle\Entity\HouseholdApp $householdApp
*
* #return DefectAct
*/
public function setHouseholdApp(\AppBundle\Entity\HouseholdApp $householdApp = null)
{
$this->householdApp = $householdApp;
return $this;
}
/**
* Get householdApp
*
* #return \AppBundle\Entity\HouseholdApp
*/
public function getHouseholdApp()
{
return $this->householdApp;
}
}
Controller:
/**
* #Security("has_role('ROLE_HOUSEHOLD')")
* #param \Symfony\Component\HttpFoundation\Request $request
* #return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function householdApplicationAction(Request $request) {
$application = $this->getUser()->getRhousehold()->getHouseholdApp();
$flag = true;
if(is_null($application)) {
$flag = false;
}
$form = $this->createForm(HouseholdAppType::class, $application, ['disabled' => false]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$appData = $form->getData();
$em = $this->get("doctrine.orm.default_entity_manager");
$household = $this->getUser()->getRhousehold();
$appData->setHousehold($household);
$em->persist($appData);
$em->flush();
return $this->redirectToRoute("householder_app");
}
return $this->render("#App/household_application.html.twig",
['form' => $form->createView(), 'householdapp' => $application]);
}
Forms:
<?php //HouseholdAppType
namespace AppBundle\Form;
use AppBundle\Entity\HouseholdApp;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
class HouseholdAppType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('multiapthouseaddress', TextType::class, ['label' => 'form.multiapthouseaddress'])
->add('cadastralnumberofbuilding', TextType::class, ['label' => 'form.cadastralnumberofbuilding'])
->add('totalbuildingarea', TextType::class, ['label' => 'form.totalbuildingarea'])
->add('house_constructive_solution', TextType::class, ['label' => 'form.house_constructive_solution'])
->add('exploitation_start_year', IntegerType::class, ['label' => 'form.exploitation_start_year'])
->add('non_residential_area', TextType::class, ['label' => 'form.non_residential_area'])
->add('has_heat_supply_system', CheckboxType::class, ['label' => 'form.has_heat_supply_system'])
->add('apts_with_individual_heating', TextType::class, ['label' => 'form.apts_with_individual_heating'])
->add('authorized_person_data', TextType::class, ['label' => 'form.authorized_person_data'])
->add('is_vatpayer', CheckboxType::class, ['label' => 'form.is_vatpayer'])
->add('personal_code', TextType::class, ['label' => 'form.personal_code'])
->add('declared_address', TextType::class, ['label' => 'form.declared_address'])
->add('contact_person_data', TextType::class, ['label' => 'form.contact_person_data'])
->add('unionRegister', VichFileType::class, ['required' => false, 'label' => 'form.unionRegister'])
->add('aptOwnersDecision', VichFileType::class, ['required' => false, 'label' => 'form.aptOwnersDecision'])
->add('multiAptInventory', VichFileType::class, ['required' => false, 'label' => 'form.multiAptInventory'])
->add('buildingTechnicalSurvey', VichFileType::class, ['required' => false, 'label' => 'form.buildingTechnicalSurvey'])
->add('defectAct', VichFileType::class, ['required' => false, 'label' => 'form.defectAct'])
->add('activityCost', VichFileType::class, ['required' => false, 'label' => 'form.activityCost'])
->add('actOfDefects', CollectionType::class, [
'label' => true,
'entry_type' => DefectsActCollectionType::class,
'allow_add' => true,
'prototype' => true,
'allow_delete' => true,
'by_reference' => false,
'delete_empty' => true,
'required' => false
])
->add('save', SubmitType::class, [
'label' => 'form.save',
'attr' => [
'class' => 'btn2'
]]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => HouseholdApp::class,
'translation_domain' => 'main',
));
}
}
<?php //DefectActCollectionType
namespace AppBundle\Form;
use AppBundle\Entity\DefectAct;
use AppBundle\Entity\HouseholdApp;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DefectsActCollectionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('defectWork', TextType::class)
->add('defectDescription', TextType::class)
->add('preventionDeadline', TextType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefaults([
'translation_domain' => 'main',
'data_class' => DefectAct::class
]);
}
public function getBlockPrefix()
{
return 'app_bundle_defects_act_collection_type';
}
}
Template:
{% extends '#App/templates/base.html.twig' %}
{% trans_default_domain "main" %}
{% block main %}
{% include '#App/templates/progressbar.html.twig' %}
{% include '#App/templates/tabs.html.twig' %}
{% include '#App/h_apply_menu.html.twig' %}
<section id='cabinet_household_app'>
<div class='container'>
<div class="tab-content">
<div id="h-apply" class="tab-pane fade in active form_page">
{{ form_start(form)}}
{{ form_row(form._token) }}
<h3>{{ 'form.house_data'|trans }}</h3>
<div class='field'><div class="form-group"> {{ form_row(form.multiapthouseaddress)}}</div></div>
<div class='field'><div class="form-group"> {{ form_row(form.cadastralnumberofbuilding)}}</div></div>
<div class='field'><div class="form-group"> {{ form_row(form.totalbuildingarea)}}</div></div>
<div class='field'><div class="form-group">{{ form_row(form.house_constructive_solution)}}</div></div>
<div class='field'><div class="form-group">{{ form_row(form.exploitation_start_year)}}</div></div>
<div class='field'><div class="form-group">{{ form_row(form.non_residential_area)}}</div></div>
<div class='field'><div class="form-group">{{ form_row(form.has_heat_supply_system, {'required': false})}}</div></div>
<div class='field'><div class="form-group">{{ form_row(form.apts_with_individual_heating)}}</div></div>
<h3>{{ 'form.app_applying_person'|trans }}</h3>
<div class='field'><div class="form-group">{{ form_row(form.authorized_person_data)}}</div></div>
<div class='field'><div class="form-group">{{ form_row(form.is_vatpayer, {'required': false})}}</div></div>
<div class='field'><div class="form-group">{{ form_row(form.personal_code)}}</div></div>
<div class='field'><div class="form-group">{{ form_row(form.declared_address)}}</div></div>
<div class='field'><div class="form-group">{{ form_row(form.contact_person_data)}}</div></div>
<h3>{{ 'form.apply_documents'|trans }}</h3>
<section id='cabinet_household_inner_app'>
{% if householdapp is null %}
<h4>{{ 'form.unionRegister'|trans }}</h4>
<div class='field'><div class="form-group">{{ form_widget(form.unionRegister) }}</div></div>
<h4>{{ 'form.aptOwnersDecision'|trans }}</h4>
<div class='field'><div class="form-group">{{ form_widget(form.aptOwnersDecision) }}</div></div>
<h4>{{ 'form.multiAptInventory'|trans }}</h4>
<div class='field'><div class="form-group">{{ form_widget(form.multiAptInventory) }}</div></div>
<h4>{{ 'form.buildingTechnicalSurvey'|trans }}</h4>
<div class='field'><div class="form-group">{{ form_widget(form.buildingTechnicalSurvey) }}</div></div>
<h4>{{ 'form.defectAct'|trans }}</h4>
<div class='field'><div class="form-group">{{ form_widget(form.defectAct) }}</div></div>
<h4>{{ 'form.activityCost'|trans }}</h4>
<div class='field'><div class="form-group">{{ form_widget(form.activityCost) }}</div></div>
{% else %}
<h4>{{ 'form.unionRegister'|trans }}</h4>
Download
<h4>{{ 'form.aptOwnersDecision'|trans }}</h4>
Download
<h4>{{ 'form.multiAptInventory'|trans }}</h4>
Download
<h4>{{ 'form.buildingTechnicalSurvey'|trans }}</h4>
Download
<h4>{{ 'form.defectAct'|trans }}</h4>
Download
<h4>{{ 'form.activityCost'|trans }}</h4>
Download
{% endif %}
<div class="container center" data-prototype="{{ form_widget(form.actOfDefects.vars.prototype)|e('html_attr') }}">
{% for actOfDefect in form.actOfDefects %}
<div class="row document">{{ form_row(actOfDefect) }}</div>
{% endfor %}
<div class="row">
<div class="col-xs-12 center">
<div id="add" class="btn2">{{ "common.addFile"|trans }}</div>
</div>
</div>
</div>
</section>
<div class="center">{{ form_row(form.save) }}</div>
{{ form_end(form, {'render_rest': false}) }}
</div>
</div>
</div>
</section>
{% endblock main %}
{% block js_bottom %}
<script>
var $collectionHolder;
$(document).ready(function () {
// Get the ul that holds the collection of tags
$collectionHolder = $('div.container.center');
// add a delete link to all of the existing tag form li elements
$collectionHolder.find('li').each(function() {
addTagFormDeleteLink($(this));
});
// add the "add a tag" anchor and li to the tags ul
//$collectionHolder.append($newLinkLi);
// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
$collectionHolder.data('index', $collectionHolder.find(':input').length);
$('#add').on('click', function (e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
addTagForm($collectionHolder);
});
});
function addTagFormDeleteLink($tagFormLi) {
var $removeFormA = $('{{ "common.cancel"|trans }}');
$tagFormLi.append($removeFormA);
$removeFormA.on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// remove the li for the tag form
$tagFormLi.remove();
});
}
function addTagForm($collectionHolder) {
var prototype = $collectionHolder.data('prototype');
// get the new index
var index = $collectionHolder.data('index');
// Replace '__name__' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(/__name__/g, index);
// increase the index with one for the next item
$collectionHolder.data('index', index + 1);
// Display the form in the page in an li, before the "Add a tag" link li
var $newFormBlock = $('<div class="row document"></div>').append(newForm);
$collectionHolder.append($newFormBlock);
addTagFormDeleteLink($newFormBlock);
}
</script>
{% endblock %}
In your class HouseholdApp, you must have :
public function addActOfDefects(\AppBundle\Entity\DefectAct $actOfDefect)
{
$this->actOfDefects[] = $actOfDefect;
$actOfDefect->setHouseholdApp($this);
return $this;
}
public function removeActOfDefects(\AppBundle\Entity\DefectAct $actOfDefect)
{
$this->actOfDefects->removeElement($actOfDefect);
}

Symfony render required on TextField true

I don't understand why the rendering of this subform doesn't render the required tag on my TextType firstName;
My form in based on a Order entity
OrderFormType has a CollectionType of Tent, based on TentFormType
TentFormType has a CollectionType of Camper, based on CamperFormType
So Order > Tent > Camper
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
//...
class CamperFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstName', TextType::class, [
'required' => true, //Should even not been usefull since SF2.8
'label' => 'First name',
'attr' => [
'placeholder' => 'First name'
],
]);
//...
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Camper',
'csrf_protection' => true,
'error_bubbling' => true,
'csrf_field_name' => '_token',
//...
]);
}
}
The fields are simply rendered with a form_widget:
{{ form_widget(form.firstName) }}
{{ form_widget(form.lastName) }}
But that not add the required field:
<input id="app_order_form_type_tents_0_campers_0_firstName" name="app_order_form_type[tents][0][campers][0][firstName]" placeholder="First name" class="form-control" type="text">
<input id="app_order_form_type_tents_0_campers_0_lastName" name="app_order_form_type[tents][0][campers][0][lastName]" placeholder="Last name" class="form-control" type="text">
I could do
{{ form_widget(form.firstName, {'attr': {'required': 'required'}}) }}
{{ form_widget(form.lastName, {'attr': {'required': 'required'}}) }}
But it shouldn't be required with my FormType...
Does anyone knows why ?
--EDIT--
My Camper Entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Camper.
*
* #ORM\Table(name="camper")
* #ORM\Entity()
*/
class Camper
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #Assert\NotBlank()
*
* #ORM\Column(name="firstName", type="string", length=255, nullable=false)
*/
private $firstName;
// ...
}
I'm sorry, i can not use comments so i put a suggestion here...
Try:
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}

FormView in Twig lose submitted data when render the form

I use the symfony 2.7.6 full stack framework.
When i submit a form, in the controller i get all submitted data, but in the view there is nothing, an empty form is rendered,something is wrong, i should display the submitted form with errors. but no data and no errors are shown in the twig form...
After hours of debuging of symfony, i can not resolve the problem.
Here is the form Type which is decalred as service:
<?php
namespace Skonsoft\ModelBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Doctrine\Common\Persistence\ObjectManager;
use Skonsoft\UserBundle\Entity\User;
class ConversationType extends AbstractType
{
/**
* #var \Doctrine\Common\Persistence\ObjectManager
*/
private $om;
/**
* #var TokenStorageInterface
*/
protected $tokenStorage;
/**
* Constructor
*
* #param ObjectManager $om
* #param TokenStorageInterface $tokenStorage
*/
public function __construct(ObjectManager $om,
TokenStorageInterface $tokenStorage)
{
$this->om = $om;
$this->tokenStorage = $tokenStorage;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('message')
->add('advert', 'entity_hidden',
array(
'class' => 'SkonsoftModelBundle:Advert',
'data_class' => null,
'data' => $options['data']->getAdvert()
))
;
$addUser = function(FormBuilderInterface $builder, User $user, $call) {
$builder->addEventListener(FormEvents::PRE_SET_DATA,
function(FormEvent $event) use ($user, $call) {
$entity = $event->getData();
$entity->$call($user);
});
};
$from = $options['data']->getFrom();
if (!$from) {
$from = $this->om->getRepository('SkonsoftUserBundle:User')->find($this->tokenStorage->getToken()->getUser()->getId());
}
$to = $options['data']->getTo();
if (!$to) {
$advert = $this->om->getRepository('SkonsoftModelBundle:Advert')->find($options['data']->getAdvert());
$to = $advert->getUser();
}
$addUser($builder, $from, 'setFrom');
$addUser($builder, $to, 'setTo');
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Skonsoft\ModelBundle\Entity\Conversation'
));
}
/**
* #return string
*/
public function getName()
{
return 'ss_conversation';
}
}
An this is the controller action:
/**
* Creates a new Conversation entity.
*
* #Route("/{id}", name="conversation_create")
* #Method("POST")
* #ParamConverter("advert", class="SkonsoftModelBundle:Advert")
* #Template("SkonsoftUserBundle:Conversation:new.html.twig")
*/
public function createAction(Request $request, Advert $advert)
{
$entity = new Conversation();
$entity->setAdvert($advert);
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
$t = $this->get('translator');
if ($form->isValid()) {
$manager = $this->get('skonsoft_model.conversation_manager');
$manager->persist($entity);
$this->addFlash('success', $t->trans('message.send.success'));
return $this->redirect($request->headers->get('referer'));
}
$this->addFlash('danger', $t->trans('message.send.error'));
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
/**
* Creates a form to create a Conversation entity.
*
* #param Conversation $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(Conversation $entity)
{
$form = $this->createForm('ss_conversation', $entity);
return $form;
}
and this is the twig:
{{form_start(form, {'action': path('conversation_create', {'id': entity.advert.id}), 'method': 'POST', 'class':'form-horizontal', 'role':'form' } )}}
<div class="form-group row">
<div class="col-sm-12 col-xs-12">
{{ form_widget(form.message, {'attr': {'class': 'form-control pull-right', 'placeholder':"message.text_holder" | trans, 'rows':'6' } }) }}
</div>
<div class="widget_error col-sm-12 col-xs-12">
{{ form_errors(form.message) }}
</div>
</div>
<div class="form-group row">
<button type="submit" class="btn btn-success btn-lg {{'css.pull.right'|trans}}">{{ "Send" | trans }} </button>
</div>
{{form_end(form)}}
When i dump the FormView in the controller, i get the submitted data and their errors... but not in the wiew
Any Body has any idea what's wrong ?
Thank you
I'm sorry, it was my mistake, not related to symfony.
So, here is the problem:
With this configuration, everything works fine, just i used the render_esi helper to render the form and i forget that render_esi is a new request to make, so when i submit the form, first master request get and process the form, but i lose all form's data after calling the render_esi helper.

Resources