Symfony 2.3.4 Implemented form not validated - forms

I have user model + user type, register model + register type... When i Execute it id doesn't validate user (inner data) model. Code below...
User model:
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Security\Core\User\UserInterface;
use Serializable;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Site\MainBundle\Entity\Repository\UserRepository")
*/
class User implements UserInterface, Serializable
{
/**
* #return array|\Symfony\Component\Security\Core\User\Role[]
*/
public function getRoles()
{
return array('ROLE_USER');
}
/**
*
*/
public function eraseCredentials()
{
}
/**
* #return string
*/
public function serialize()
{
return serialize(array($this->id));
}
/**
* #param string $serialized
*/
public function unserialize($serialized)
{
list ($this->id,) = unserialize($serialized);
}
/**
*
*/
public function __construct()
{
$this->isActive = false;
$this->salt = md5(uniqid(null, true));
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
public function getID()
{
return $this->id;
}
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=255, nullable=false)
* #Assert\NotBlank(message="User name cannot be blank.")
*/
private $username;
public function setUserName($userName)
{
$this->username = $userName;
return $this;
}
public function getUserName()
{
return $this->username;
}
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255, nullable=false)
* #Assert\NotBlank(message="Password cannot be blank.")
*/
private $password;
public function setPassword($password)
{
$this->password = $password;
return $this;
}
public function getPassword()
{
return $this->password;
}
/**
* #var string
*
* #ORM\Column(name="salt", type="string", length=255, nullable=false)
*/
private $salt;
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
public function getSalt()
{
return $this->salt;
}
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, nullable=false)
* #Assert\NotBlank(message="E-Mail cannot be blank.")
* #Assert\Email(message="Invalid email address.")
*/
private $email;
public function setEmail($email)
{
$this->email = $email;
return $this;
}
public function getEmail()
{
return $this->email;
}
/**
* #var bool
*
* #ORM\Column(name="isActive", type="boolean", nullable=false)
*/
private $isActive;
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
public function getIsActive()
{
return $this->isActive;
}
}
User Type:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', 'text', array('label' => 'User name'))
->add(
'password',
'repeated',
array(
'label' => 'Password',
'first_name' => 'password',
'second_name' => 'confirm',
'type' => 'password'
)
)
->add('email', 'text', array('label' => 'E-Mail'));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Site\MainBundle\Entity\User',
'required' => false
)
);
}
/**
* #return string
*/
public function getName()
{
return 'main_user';
}
}
Register model:
use Symfony\Component\Validator\Constraints as Assert;
use Site\MainBundle\Entity\User;
/**
* Register
*/
class Register
{
/**
* #var Site\MainBundle\Entity\User
*
* #Assert\Type(type="Site\MainBundle\Entity\User")
* #Assert\Valid()
*/
protected $user;
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
public function getUser()
{
return $this->user;
}
/**
* #var boolean
*
* #Assert\NotBlank(message="No terms accepted.")
* #Assert\True(message="You have to accept terms to be registered.")
*/
protected $terms;
public function setTerms($terms)
{
$this->terms = (Boolean)$terms;
return $this;
}
public function getTerms()
{
return $this->terms;
}
}
Register Type:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Site\MainBundle\Form\UserType;
class RegisterType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user', new UserType())
->add('terms', 'checkbox');
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Site\MainBundle\Entity\Register',
'required' => false
)
);
}
/**
* #return string
*/
public function getName()
{
return 'main_register';
}
}
And controller with twig. Part of twig:
{% block page_content %}
<form method="post" action="{{ path('main_register') }}">
{{ form_errors(formRegister.user.username) }}
{{ form_row(formRegister.user.username) }}
{{ form_errors(formRegister.user.password.password) }}
{{ form_row(formRegister.user.password.password) }}
{{ form_errors(formRegister.user.password.confirm) }}
{{ form_row(formRegister.user.password.confirm) }}
{{ form_errors(formRegister.user.email) }}
{{ form_row(formRegister.user.email) }}
{{ form_errors(formRegister.terms) }}
{{ form_row(formRegister.terms) }}
<button type="submit">Register</button>
</form>
{% endblock %}
Register Controller:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Site\MainBundle\Form\RegisterType;
use Site\MainBundle\Entity\Register;
/**
* RegisterController
*/
class RegisterController extends Controller
{
public function defaultAction()
{
$formRegister = $this->createForm(new RegisterType(), new Register());
if ($this->getRequest()->isMethod('post')) {
$formRegister->handleRequest($this->getRequest());
if ($formRegister->isValid()) {
echo 'form is valid';
}
}
$twig = 'SiteMainBundle:register:default.html.twig';
$data = array(
'formRegister' => $formRegister->createView()
);
return $this->render($twig, $data);
}
}
Where can be problem? Help please with it.
Updated
echo "<pre>";
echo $formRegister->getErrorsAsString();
echo '</pre>';
says:
user:
username:
No errors
password:
password:
No errors
confirm:
No errors
email:
No errors
terms:
ERROR: No terms accepted.
ERROR: You have to accept terms to be registered.
As you can see model User is not validated AT ALL... =\

Found, finally.
Bug is in this part. I wanted to disable this way browser validation...
'required' => false
BUT. This "guys" (I mean developers),compare two things in one setting. So if we set 'required' to false our fields won't be validated. And if true it will be validate BUT ALSO WITH browser validator.. =\ Maybe someone know how to avoid it (browser validation) NOT by editing twig file (some options or other setting)?
PS: To avoid browser i changed twig.
<form method="post" action="{{ path('main_register') }}" novalidate>

If you wanna do it by code, in your Controller, your code should be like:
class RegisterController extends Controller
{
public function defaultAction()
{
$formRegister = $this->createForm(new RegisterType(), new Register(), array(
'attr'=> array('novalidate'=>'novalidate'),
));
With the therd parámeter of "createForm", you can modified the form (for example: acction, class, etc).
I normaly use:
$form = $this->createForm(new RegistroType(), $usuario, array(
'action' => $this->generateUrl('jgo_registro'),
'method' => 'POST',
'attr'=> array('novalidate'=>'novalidate'),
));

Related

Symfony 4 form: three dynamic select box

Hello I am using Symfony 4.
I have managed to link up to two select box with form events, but I need to have three dynamic select box.
This is the relation between my entities:
Country -> Province -> City.
These are linked to a Person entity like this
When I add a new person I should be able to select a Country and have the Province dropdown updated in accordance to Country selection; same thing for the City dropdown after I have selected a Province.
I have made things working for Country and Province following the official Symfony guide here
https://symfony.com/doc/current/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms
How should I manage adding the third dropdown?
This is my Country entity:
<?php
namespace App\Entity\Geo;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass="App\Repository\Geo\CountryRepository")
*/
class Country
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Geo\Province", mappedBy="country")
* #ORM\JoinColumn(nullable=false)
*/
private $provinces;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Geo\City", mappedBy="country")
* #ORM\JoinColumn(nullable=false)
*/
private $cities;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Geo\Person", mappedBy="country")
* #ORM\JoinColumn(nullable=false)
*/
private $persons;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name): void
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getProvinces()
{
return $this->provinces;
}
/**
* #param mixed $provinces
*/
public function setProvinces($provinces): void
{
$this->provinces = $provinces;
}
/**
* #return mixed
*/
public function getCities()
{
return $this->cities;
}
/**
* #param mixed $cities
*/
public function setCities($cities): void
{
$this->cities = $cities;
}
/**
* #return mixed
*/
public function getPersons()
{
return $this->persons;
}
/**
* #param mixed $persons
*/
public function setPersons($persons): void
{
$this->persons = $persons;
}
}
This is my Province entity:
<?php
namespace App\Entity\Geo;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass="App\Repository\Geo\ProvinceRepository")
*/
class Province
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Geo\Country", inversedBy="provinces")
* #ORM\JoinColumn(nullable=false)
*/
private $country;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Geo\City", mappedBy="province")
* #ORM\JoinColumn(nullable=false)
*/
private $cities;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Geo\Person", mappedBy="province")
* #ORM\JoinColumn(nullable=false)
*/
private $persons;
public function __toString() {
return $this->name;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name): void
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getCountry()
{
return $this->country;
}
/**
* #param mixed $country
*/
public function setCountry($country): void
{
$this->country = $country;
}
/**
* #return mixed
*/
public function getCities()
{
return $this->cities;
}
/**
* #param mixed $cities
*/
public function setCities($cities): void
{
$this->cities = $cities;
}
/**
* #return mixed
*/
public function getPersons()
{
return $this->persons;
}
/**
* #param mixed $persons
*/
public function setPersons($persons): void
{
$this->persons = $persons;
}
}
This is my City entity:
<?php
namespace App\Entity\Geo;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass="App\Repository\Geo\CityRepository")
*/
class City
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Geo\Province", inversedBy="cities")
* #ORM\JoinColumn(nullable=false)
*/
private $province;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Geo\Country", inversedBy="cities")
* #ORM\JoinColumn(nullable=false)
*/
private $country;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Geo\Person", mappedBy="city")
* #ORM\JoinColumn(nullable=false)
*/
private $persons;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name): void
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getProvince()
{
return $this->province;
}
/**
* #param mixed $province
*/
public function setProvince($province): void
{
$this->province = $province;
}
/**
* #return mixed
*/
public function getCountry()
{
return $this->country;
}
/**
* #param mixed $country
*/
public function setCountry($country): void
{
$this->country = $country;
}
/**
* #return mixed
*/
public function getPersons()
{
return $this->persons;
}
/**
* #param mixed $persons
*/
public function setPersons($persons): void
{
$this->persons = $persons;
}
}
This is my form to add a Person (PersonType.php)
<?php
namespace App\Form\Geo;
use App\Entity\Geo\Person;
use App\Entity\Geo\Country;
use App\Entity\Geo\Province;
use App\Entity\Geo\City;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, ['label' => "Name"])
->add('country', EntityType::class, [
'class' => Country::class,
'choice_label' => function(Country $country) {
return $country->getName();
},
'placeholder' => 'Choose a Country'
])
;
$formModifier = function (FormInterface $form, Country $country = null) {
$provinces = null === $country ? [] : $country->getProvinces();
$form->add('province', EntityType::class, [
'class' => Province::class,
'placeholder' => 'Choose a Province',
'choices' => $provinces,
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
$formModifier($event->getForm(), $data->getCountry());
}
);
$builder->get('country')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$country = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $country);
}
);
$builder->add( 'save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' =>Person::class
]);
}
}
This is my twig template (person-add.html.twig)
{% extends 'base.html.twig' %}
{% block title %}Add Person{% endblock %}
{% block body %}
{{ form_start(form) }}
{{ form_row(form.name) }}
{{ form_row(form.country) }}
{{ form_row(form.province) }}
{{ form_end(form) }}
<script>
$(document).ready(function() {
var $country = $('#person_country');
// When sport gets selected ...
$country.change(function () {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected sport value.
var data = {};
data[$country.attr('name')] = $country.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 ...
$('#person_province').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#person_province')
);
// Position field now displays the appropriate positions.
}
});
})
});
</script>
{% endblock %}
Thanks to this post I have managed to change my PersonType.php form file like this:
<?php
namespace App\Form\Geo;
use App\Entity\Geo\Person;
use App\Entity\Geo\Country;
use App\Entity\Geo\Province;
use App\Entity\Geo\City;
use App\Repository\Geo\CityRepository;
use App\Repository\Geo\ProvinceRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
//
->add('country', EntityType::class, [
'class' => Country::class,
'label' => 'Country',
'required' => true,
'choice_label' => function(Country $country) {
return $country->getName();
},
'invalid_message' => 'You must select a Country',
'placeholder' => 'Select Country',
]);
//**************** Start Province Form
$addProvinceForm = function (FormInterface $form, $country_id) {
// it would be easier to use a Park entity here,
// but it's not trivial to get it in the PRE_SUBMIT events
$form->add('province', EntityType::class, [
'class' => Province::class,
'label' => 'Province',
'required' => true,
'invalid_message' => 'Choose a Province',
'placeholder' => null === $country_id ? 'Choose a Country first' : 'Select Province',
'query_builder' => function (ProvinceRepository $repository) use ($country_id) {
return $repository->createQueryBuilder('p')
->innerJoin('p.country', 'c')
->where('c.id = :country')
->setParameter('country', $country_id)
;
}
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($addProvinceForm) {
$country = $event->getData()->getCountry();
$country_id = $country ? $country->getId() : null;
$addProvinceForm($event->getForm(), $country_id);
}
);
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($addProvinceForm) {
$data = $event->getData();
$country_id = array_key_exists('country', $data) ? $data['country'] : null;
$addProvinceForm($event->getForm(), $country_id);
}
);
//**************** End Province Form
//**************** Start City Form
$addCityForm = function (FormInterface $form, $province_id) {
$form->add('city', EntityType::class, [
'class' => City::class,
'label' => 'City',
'required' => true,
'invalid_message' => 'You must choose a City',
'placeholder' => null === $province_id ? 'Choose a Province first' : 'Choose a City',
'query_builder' => function (CityRepository $repository) use ($province_id) {
return $repository->createQueryBuilder('ci')
->innerJoin('ci.province', 'pr')
->where('pr.id = :province')
->setParameter('province', $province_id)
;
}
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($addCityForm) {
$province = $event->getData()->getProvince();
$province_id = $province ? $province->getId() : null;
$addCityForm($event->getForm(), $province_id);
}
);
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($addCityForm) {
$data = $event->getData();
$province_id = array_key_exists('province', $data) ? $data['province'] : null;
$addCityForm($event->getForm(), $province_id);
}
);
//**************** End City Form
$builder->add( 'save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' =>Person::class
]);
}
}
The Province dropdown works as expected when you first select a Country.
The problem is the City dropdown: nothing changes after you select a Province.
If everything is ok with the query executed inside the PersonType.php file, I think I am doing something wrong with the javascript. Here's my code:
<script>
$(document).ready(function() {
var $country = $('#person_country');
var $province = $('#person_province');
// When country gets selected ...
$country.change(function () {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected country value.
var data = {};
data[$country.attr('name')] = $country.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 province field ...
$('#person_province').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#person_province')
);
}
});
});
// When province gets selected ...
$province.change( function () {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected province value.
var data = {};
data[$province.attr('name')] = $province.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 city field ...
$('#person_city').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#person_city')
);
}
});
});
});
</script>

Symfony Form EntityType

I'am building a simple product page. Some products could have some extra options (productOptions). These options could be selected due to selectboxes. But the problem is as follows:
I've the following Entities:
Product
#src/appbundle/entity
<?php
/**
* Product
*/
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository")
* #ORM\Table(name="product")
*/
class Product {
/**
* #var string id
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var string title
* #ORM\Column(type="string")
*/
protected $title;
/**
* #var ArrayCollection|ProductOption[]
* #ORM\OneToMany(targetEntity="AppBundle\Entity\ProductOption", mappedBy="product", fetch="EAGER")
*/
protected $productOptions;
protected $tempProductOptions;
/**
* #return string
*/
public function getId() {
return $this->id;
}
/**
* #return string
*/
public function getTitle() {
return $this->title;
}
/**
* #param string $title
*/
public function setTitle($title) {
$this->title = $title;
}
public function __construct() {
$this->productOptions = new ArrayCollection();
}
public function addProductOption(ProductOption $productOption){
if($this->productOptions->contains($productOption)){
return;
}
$productOption->setProduct($this);
$this->productOptions->add($productOption);
}
public function removeProductOption(ProductOption $productOption){
if(!$this->productOptions->contains($productOption)){
return;
}
$productOption->setProduct(null);
$this->productOptions->removeElement($productOption);
}
/**
* #return ProductOption[]|ArrayCollection
*/
public function getProductOptions() {
return $this->productOptions->toArray();
}
}
ProductOption
#src/appbundle/entity
<?php
/**
* ProductOption
*/
namespace AppBundle\Entity;
use AppBundle\Repository\GenusRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\ProductOptionRepository")
* #ORM\Table(name="product_option")
*/
class ProductOption {
/**
* #var int id
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var string title
* #ORM\Column(type="string")
*/
protected $title;
/**
* #var float price
* #ORM\Column(type="float", scale=2)
*/
protected $price;
/**
* ProductOption connected to specific product
* #ORM\ManyToOne(targetEntity="Product", inversedBy="productOptions")
* #ORM\JoinColumn(nullable=false)
*/
protected $product;
/**
* #return int
*/
public function getId() {
return $this->id;
}
/**
* #param int $id
*/
public function setId($id) {
$this->id = $id;
}
/**
* #return string
*/
public function getTitle() {
return $this->title;
}
/**
* #param string $title
*/
public function setTitle($title) {
$this->title = $title;
}
/**
* #return float
*/
public function getPrice() {
return $this->price;
}
/**
* #param float $price
*/
public function setPrice($price) {
$this->price = $price;
}
/**
* #return Product
*/
public function getProduct() {
return $this->product;
}
/**
* #param Product $product
*/
public function setProduct($product) {
$this->product = $product;
}
public function __toString() {
return (string)$this->getTitle() . '(€'.$this->getPrice().')';
}
}
This is my ProductType (formbuilder)
#src/appbundle/form
<?php
namespace AppBundle\Form;
use AppBundle\Entity\Product;
use AppBundle\Entity\ProductOption;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
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;
class ProductType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('title', TextType::class, [
'label' => 'titel'
]);
$builder->add('tempProductOptions', EntityType::class, [
'class' => ProductOption::class,
'multiple' => true,
'expanded' => true,
'mapped' => false,
'choice_attr' => function($productOption, $key, $index) {
/** #var ProductOption $productOption */
return [
'data-price' => $productOption->getPrice(),
];
},
]);
$builder->add('save', SubmitType::class, array(
'label' => 'Opslaan'
));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => Product::class,
));
}
public function getBlockPrefix() {
return 'product';
}
}
Twig file
its a very basic example
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>test product page</h1>
{{ dump(form) }}
{{ form_start(form) }}
{{ form_row(form.titel) }}
{{ form_row(form.productOptions) }}
{{ form_row(form.save) }}
{{ form_end(form) }}
</body>
</html>
For exmaple product 1 has two options:
option 1 (50 euro)
option 2 (60 euro)
In the database option 1 and 2 are connected to product 1
table product_option
ID title price product_id
1 option 1 50 1
2 option 2 60 1
But in the view the options are already checked (because they connected through db). Is it possible to uncheck the checkboxes.
Short version. Each product could have extra options. These options are stored in db. When creating the form on product page the user could choose for these options, but they are already checked.
Also added the following to my formbuilder
public function finishView(FormView $view, FormInterface $form, array $options) {
foreach ($view->children['productOptions']->children as $productOption) {
$productOption->vars['checked'] = false;
}
}
but the checkboxes are still being checked
I've found an solution:
public function buildForm(FormBuilderInterface $builder, array $options) {
// arraycollection with productOptions
$productOptions = $options['data']->getProductOptions();
$builder->add('id', HiddenType::class, ['mapped' => false]);
$builder->add('productOptions', ChoiceType::class, [
'choices' => $productOptions,
'expanded' => true,
'multiple' => true,
'mapped' => false,
'choice_value' => function (ProductOption $productOption) {
return $productOption->getId();
},
'choice_label' => function(ProductOption $productOption, $key, $index) {
return $productOption->__toString();
},
'choice_attr' => function(ProductOption $productOption, $key, $index) {
return [
'data-prijs' => $productOption->getPrijs(),
];
},
]);
// rest here ...

form whith createForm doesn't work and i can't get assert message

I already have to ask a question about a similar assert problem with FosUserBundle configuation.
But now I'm trying to run a simple form, but even with a esiest form => nothing is happening.
when i click on submit :
1) isValid() stay to false
2) No Assert message appears when input name is blank/empty
UserTmp.php (entity)
<?php
namespace BISSAP\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* BISSAP\UserBundle\Entity\User
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="BISSAP\UserBundle\Entity\UserRepository")
*/
class Usertmp
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="name", type="string", length=255)
*
* #Assert\NotBlank(message="Please enter your name.", groups={"Registration", "Profile"})
* #Assert\Length(
* min=3,
* max=255,
* minMessage="The name is too short.",
* maxMessage="The name is too long.",
* groups={"Registration", "Profile"}
* )
*/
private $name;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Usertmp
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
}
UserType.php
<?php
namespace BISSAP\ForumBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text', array('required' => false))
->add('Envoyer', 'submit', array(
'attr' => array(
'class' => 'btn right-flt'
)));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BISSAP\UserBundle\Entity\Usertmp'
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array( 'data_class' => $this->class, 'intention' => 'Registration', ));
}
/**
* #return string
*/
public function getName()
{
return 'bissap_forumbundle_user';
}
}
TController.php
<?php
namespace BISSAP\ForumBundle\Controller;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use BISSAP\ForumBundle\Form\UserType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use BISSAP\UserBundle\Entity\Usertmp;
class TController extends Controller
{
public function indexAction()
{
$entity = new Usertmp();
$form = $this->createForm(new UserType(), $entity);
if ($form->isValid())
{
return $this->redirectToRoute('bissap_forum_index');
}
return $this->render('BISSAPForumBundle:T:index.html.twig', array('form'=> $form->createView(), 'errors_tmp' => $this->getErrorMessages($form)));
}
private function getErrorMessages(\Symfony\Component\Form\Form $form)
{
$errors = array();
foreach ($form->getErrors(true, false) as $error) {
// My personnal need was to get translatable messages
// $errors[] = $this->trans($error->current()->getMessage());
$errors[] = $error->current()->getMessage();
}
return $errors;
}
}
?>
index.html.twig
----> {{form( form )}}
{% for error in errors_tmp %}
<div>error : {{ error }}</div>
{% endfor %}
So, form didn't work cause : $form->handleRequest($request); missed in TController.php
In your UserType.php, try:
$resolver->setDefaults(array( 'data_class' => $this->class, 'intention' => 'Registration', 'validation_groups' => array('registration'),));
You can also set the validation group in your TController.php instead of hard-coding it on your UserType:
$form = $this->createForm(new UserType(), $entity, array('validation_groups' => 'registration'));
Source: Validation Groups
Also in your UserType.php you are saying that the name is not required, but at the same time you want to assert if it is not blank:
->add('name', 'text', array('required' => false))
Try removing that option too.

Symfony2 (2.7) Form Entity Data Transformer

I'm trying to customize a selection list's text while using the entity's ID. This is because I want the list options to be specific to the authenticated user. The database text values are Full Name, By City and State, and Anonymous, but I want it to actually display the user's full name (John Smith), User in Denver, CO, and Anonymous. I'm attempting to use a view data transformer to achieve this, but with no luck. I'd rather not use Javascript to achieve this if possible.
Here's my main form type:
<?php
namespace Members\MessagesBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\SecurityContext;
class MessageType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('viewability', 'viewability_entity', array(
'class' => 'MessagesBundle:Viewability',
'property' => 'name',
'required' => true,
))
->add('body', new MessageBodyType())
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Members\MessagesBundle\Entity\Message',
));
}
/**
* #return string
*/
public function getName()
{
return 'members_messages_message';
}
}
Here's my custom form type for Viewability (the entity which I would like to transform):
<?php
namespace Members\MessagesBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\SecurityContext;
use Members\MessagesBundle\Form\DataTransformer\MessageNameTransformer;
class ViewabilityType extends AbstractType
{
private $context;
/**
* #param SecurityContext $context
*/
public function __construct(SecurityContext $context)
{
$this->context = $context;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new MessageNameTransformer($this->context);
$builder->addViewTransformer($transformer);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'invalid_message' => 'The selected issue does not exist',
));
}
/**
* #return string
*/
public function getParent()
{
return 'entity';
}
/**
* #return string
*/
public function getName()
{
return 'viewability_entity';
}
}
Here's my service which defines the Viewability Type:
members.messages.form.type.viewability_entity:
class: Members\MessagesBundle\Form\ViewabilityType
tags:
- { name: form.type, alias: viewability_entity }
arguments: [#security.context]
Here's my Viewability Entity:
<?php
namespace Members\MessagesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
*/
class Viewability
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
public function __construct()
{
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
}
Finally, here's my data transformer:
<?php
namespace Members\MessagesBundle\Form\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
use Members\MessagesBundle\Entity\Viewability;
use Symfony\Component\Security\Core\SecurityContext;
class MessageNameTransformer implements DataTransformerInterface
{
private $user;
/**
* #param SecurityContext $context
*/
public function __construct(SecurityContext $context)
{
$this->user = $context->getToken()->getUser();
}
/**
* #param Viewability|null $viewability
* #return string
*/
public function transform($viewability)
{
if (null === $viewability) {
return '';
}
if($viewability === 'Full Name')
return sprintf('%s %s', $this->user->getInfo()->getFirstName(), $this->user->getInfo()->getLastName());
if($viewability === 2)
return sprintf('Lawyer in %s, %s', $this->user->getInfo()->getAddress()->getCity(), $this->user->getInfo()->getAddress()->getState());
if($viewability === 3)
return 'Anonymous';
}
/**
* #param Viewability $viewability
* #return Viewability
*/
public function reverseTransform($viewability)
{
return $viewability;
}
}
The data passed into transform() always seems to be null or "" (empty string).
Thanks for any help.
So I ended up taking a different approach to solving this. Originally I was trying to transform data coming from an entity. Fortunately this entity didn't really need to be a database entity after all and a simple choice type sufficed. This doesn't solve the specific issue of transforming an entity list, but it allows me to customize the drop down list values.
The viewability entity was removed and the relationship in the Message entity was changed to an integer field.
My main type is now as follows:
class MessageType extends AbstractType
{
private $user;
public function __construct($user)
{
$this->user = $user;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('body', new MessageBodyType())
->add('viewability', 'choice', array(
'choices' => array(
1 => $user->getFirstName(),
2 => $user->getAddress()->getCity(),
3 => 'Anonymous',
),
'multiple' => false,
'label' => 'Send Message As',
'data' => 0,
))
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Members\MessagesBundle\Entity\Message',
));
}
/**
* #return string
*/
public function getName()
{
return 'members_messages_message';
}
}

Doctrine Mongo document + Embed a Collection of Forms

Project with symfony 2 and mongoDB.
I'm following this tutorial: http://symfony.com/doc/current/cookbook/form/form_collections.html
But once I save the form I get this error:
Cannot create a DBRef, the document is not an object
Line of crash:
https://github.com/doctrine/mongodb-odm/blob/master/lib/Doctrine/ODM/MongoDB/DocumentManager.php#L691
Form code:
namespace Fonts\FontsBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class FamilyType extends AbstractType
{
public function __construct($dm)
{
$this->dm = $dm;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'text', array('max_length' => 50, 'error_bubbling' => true));
$builder->add('fonts', 'collection', array(
'type' => new FontType($this->dm),
'allow_add' => true,
'by_reference' => false,
));
}
public function getName()
{
return 'Family';
}
}
Controller code:
namespace Fonts\FontsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Fonts\FontsBundle\Document\Family;
use Fonts\FontsBundle\Document\Font;
use Fonts\FontsBundle\Form\Type\FamilyType;
class FamilyBackendController extends BaseController
{
public function newAction()
{
try {
$family = new Family();
$form = $this->createForm(new FamilyType($this->getMongoService()), $family);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$this->persist($family);
$this->get('session')->setFlash('notice', 'Item successfully created.');
return ($request->request->get('save') === 'Save') ?
new RedirectResponse($this->generateUrl('backend_familys_list')) :
new RedirectResponse($this->generateUrl('backend_familys_new'));
}
}
return $this->render('FontsBundle:Backend:newFamily.html.twig', array(
'form' => $form->createView(),
));
} catch(\Exception $e) {
return new Response($e->getMessage());
}
}
}
Document:
<?php
namespace Fonts\FontsBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Fonts\FontsBundle\Service\SlugService;
/**
* #MongoDB\Document(repositoryClass="Fonts\FontsBundle\Repository\FamilyRepository")
*/
class Family
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
* #MongoDB\UniqueIndex(safe=true)
* #Assert\NotBlank(message="FamilyName value should not be blank.")
* #Assert\MinLength(limit=3,message="FamilyName must have at least {{ limit }} characters.")
* #Assert\MaxLength(limit=50,message="FamilyName must have maximum {{ limit }} characters.")
*/
protected $name;
/**
* #MongoDB\ReferenceMany(targetDocument="Font", simple=true)
* #Assert\NotBlank(message="Fonts should not be blank.")
*/
protected $fonts;
/**
* #MongoDB\String
* #MongoDB\UniqueIndex(safe=true)
*/
protected $slug;
/**
* #MongoDB\Int
*/
protected $createdAt;
public function __construct()
{
$this->fonts = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return id $id
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Family
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string $name
*/
public function getName()
{
return $this->name;
}
/**
* Add fonts
*
* #param Fonts\FontsBundle\Document\Font $fonts
*/
public function addFonts(\Fonts\FontsBundle\Document\Font $fonts)
{
$this->fonts[] = $fonts;
}
/**
* Set fonts
*
* #param Doctrine\Common\Collections\Collection $fonts
*/
public function setFonts($fonts)
{
$this->fonts = $fonts;
}
/**
* Get fonts
*
* #return Doctrine\Common\Collections\Collection $fonts
*/
public function getFonts()
{
return $this->fonts;
}
/**
* Set slug
*
* #param string $slug
* #return Family
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string $slug
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set createdAt
*
* #param int $createdAt
* #return Family
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* #return int $createdAt
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* #MongoDB\PrePersist
*/
public function prePersist()
{
$this->setCreatedAt(time());
$slugService = new SlugService();
$this->setSlug($slugService->slug($this->getName()));
}
/**
* #MongoDB\PreUpdate
*/
public function preUpdate()
{
$slugService = new SlugService();
$this->setSlug($slugService->slug($this->getName()));
}
}
The form crash when I try to persist the object in the controller action_
$this->persist($family);
I tried lot of options but no one with good results. If you have some idea, please reply.