So I (think I have done it correctly) mapped a one to many relation between two table.
The SQL to create foreign key was generated but able.the field on my admin class show form fields method is not outputting a field from the other table.
My Main Entity:
/**
* Suppliers
*
* #ORM\Table(
* name="suppliers",
* indexes={
* #Index(
* name="supplier_name", columns={"name"}
* )
* })
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class Suppliers
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false, options={"unsigned":true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #Assert\NotNull()
* #ORM\Column(name="type", type="string", nullable=false)
*/
private $type;
/**
* #var string
*
* #Assert\NotNull()
* #ORM\Column(name="name", type="string", nullable=false, nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
/**
* #var string
*
* #ORM\Column(name="updated", nullable=true, type="datetime")
*/
private $updated;
/**
* #var string
*
* #ORM\Column(name="deleted", type="boolean", options={"default":0})
*/
private $deleted;
/**
* #var
*
* #ORM\OneToMany(targetEntity="AppNamespace\Bundle\SupplierBundle\Entity\Region\Region", mappedBy="supplies")
*/
private $regions;
/**
* #var string
*/
private $entityName;
public function __construct()
{
$this->regions = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set type
*
* #param string $type
* #return User
*/
public function setType($type)
{
$this->type = strtolower($type);
return $this;
}
/**
* Get type
*
* #return string
*/
public function getType()
{
return strtoupper($this->type);
}
/**
* Set name
*
* #param string $name
* #return User
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set created
*
* #param string $created
* #return $this
*/
public function setCreated($created)
{
if (!empty($created)) {
$this->created = $created;
} else {
$this->created = new \DateTime("now");
}
return $this;
}
/**
* Get created
*
* #return string
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* #param string $updated
* #return User
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* #return string
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set deleted
*
* #param boolean $deleted
* #return User
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
/**
* Get deleted
*
* #return boolean
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* #ORM\PrePersist()
*/
public function onPrePersist()
{
$this->created = new \DateTime();
$this->updated = new \DateTime();
if ($this->deleted === null) {
$this->deleted = 0;
}
}
/**
* #ORM\PreUpdate()
*/
public function onPreUpdate()
{
$this->created = new \DateTime();
$this->updated = new \DateTime();
if ($this->deleted === null) {
$this->deleted = 0;
}
}
/**
* #return array
*/
public static function getTypes()
{
return array(
'internal' => 'Internal',
'external' => 'External',
'lpe' => 'LPE'
);
}
/**
* Set entityName
*
* #param string $name
* #return User
*/
public function setEntityName($name)
{
$this->entityName = $name;
return $this;
}
public function __toString()
{
if (!empty($this->entityName) && !empty($this->id)) {
return $this->entityName;
} else if (!empty($this->id)) {
return 'Supplier #'. $this->getId();
}
return 'Supplier';
}
}
Schema:
CREATE TABLE `suppliers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created` datetime NOT NULL,
`updated` datetime DEFAULT NULL,
`deleted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `supplier_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Supplier regions join table:
/**
* Region
*
* #ORM\Table(
* name="supplier_regions",
* indexes={
* #Index(
* name="supplier_regions_postcode", columns={"postcode"}
* )
* })
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class Region
{
/**
* #var integer
*
* #ORM\Column(name="supplier_id", type="integer", nullable=false, options={"unsigned":true})
* #ORM\Id
*/
private $supplierId;
/**
* #var integer
*
* #ORM\Column(name="postcode", type="string", nullable=false)
* #ORM\Id
*/
private $postcode;
/**
* #var string
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
/**
* #var
*
* #ORM\ManyToOne(targetEntity="AppNamespace\Bundle\SupplierBundle\Entity\Suppliers", inversedBy="regions")
* #ORM\JoinColumn(name="supplier_id", referencedColumnName="id")
*/
private $supplies;
/**
* Get supplierId
*
* #return integer
*/
public function getSupplierId()
{
return $this->supplierId;
}
/**
* Set supplierId
*
* #param integer $supplierId
* #return $this
*/
public function setSupplierId($supplierId)
{
$this->supplierId = $supplierId;
return $this;
}
/**
* Get postcode
*
* #return integer
*/
public function getPostcode()
{
return $this->postcode;
}
/**
* Set postcode
*
* #param integer $postcode
* #return $this
*/
public function setPostcode($postcode)
{
$this->postcode = $postcode;
return $this;
}
/**
* Set created
*
* #param string $created
* #return $this
*/
public function setCreated($created)
{
if (!empty($created)) {
$this->created = $created;
} else {
$this->created = new \DateTime("now");
}
return $this;
}
/**
* Get created
*
* #return string
*/
public function getCreated()
{
return $this->created;
}
/**
* #param Supplier $supplies
* #return $this
*/
public function setSupplied(Supplier $supplies = null)
{
$this->supplies = $supplies;
return $this;
}
/**
* Get supplies
*
* #return Supplier
*/
public function getSupplied()
{
return $this->supplies;
}
}
Schema:
CREATE TABLE `supplier_regions` (
`supplier_id` int(10) unsigned NOT NULL,
`postcode` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`supplier_id`,`postcode`),
KEY `supplier_regions_postcode` (`postcode`),
KEY `IDX_23020DEC2ADD6D8C` (`supplier_id`),
CONSTRAINT `FK_23020DEC2ADD6D8C` FOREIGN KEY (`supplier_id`) REFERENCES `suppliers` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
So when Sonata goes to show form I would like it to run the below query behind the scenes:
SELECT
*
FROM
supplier_regions sr
JOIN suppliers s ON s.id = sr.supplier_id
Here is my Supplier admin class:
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Route\RouteCollection;
use Sonata\AdminBundle\Form\Type;
use Sonata\AdminBundle\Validator\ErrorElement;
use Housesimple\Bundle\CoreBundle\Util\Request as RequestHelper;
use Housesimple\Bundle\SupplierBundle\Entity\Suppliers as Suppliers;
use Knp\Menu\ItemInterface as MenuItemInterface;
class SupplierAdmin extends Admin
{
protected $parentAssociationMapping = 'admin';
/**
* #var \Symfony\Component\DependencyInjection\ContainerInterface
*/
private $container;
public function setContainer (\Symfony\Component\DependencyInjection\ContainerInterface $container)
{
$this->container = $container;
}
/**
* #param \Sonata\AdminBundle\Show\ShowMapper $showMapper
*
* #return void
*/
protected function configureShowFields(ShowMapper $showMapper)
{
dump($this->getSubject());
$showMapper
->add('id')
->add('type')
->add('name')
->add('created')
->add('updated')
->end()
->with('Regions')
->add('region', 'sonata_type_collection', array(
'by_reference' => false // true doesn't work neither
))
->end();
}
/**
* #param \Sonata\AdminBundle\Form\FormMapper $formMapper
*
* #return void
*/
protected function configureFormFields(FormMapper $formMapper)
{
$actionName = RequestHelper::getActionName($this->getRequest());
switch ($actionName) {
case 'create':
$em = $this->container->get('doctrine.orm.entity_manager');
$lastId = $em->getConnection()->lastInsertId();
$nextId = (empty($lastId) ? '' : ' #'. $lastId + 1);
$this->getSubject()->setEntityName('New supplier #' . $nextId);
$formMapper->with('New supplier'. $nextId);
break;
case 'edit':
$this->supportsPreviewMode = true;
$formMapper->with('Edit Supplier #'. $this->getSubject()->getId());
break;
}
$formMapper
->add('type', 'choice', array(
'choices' => Suppliers::getTypes(),
'empty_value' => 'Choose an option',
'required' => true
))
->add('name')
->end();
}
/**
* {#inheritdoc}
*/
protected function configureSideMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
{
/*$admin = $this->isChild() ? $this->getParent() : $this;
$menuItems = array('list', 'create');
$menu->addChild('action', array('attributes' => array('dropdown' => true)));
foreach ($menuItems as $item) {
if ($item == $action) {
continue;
}
$menu['action']->addChild(
ucwords($item),
array('uri' => $admin->generateUrl(strtolower($item)))
);
}
return $menu;*/
}
/**
* #param \Sonata\AdminBundle\Datagrid\ListMapper $listMapper
*
* #return void
*/
protected function configureListFields(ListMapper $listMapper)
{
//unset($this->listModes['mosaic']);
$listMapper
->add('id')
->add('type')
->addIdentifier('name')
->add('created', 'date', array(
'pattern' => 'dd/MM/Y # H:m',
'locale' => 'en_GB',
'timezone' => 'Europe/London',
))
// add custom action links
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array()
)
));
}
/**
* #param \Sonata\AdminBundle\Datagrid\DatagridMapper $datagridMapper
*
* #return void
*/
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('type')
->add('name')
->add('created')
->add('updated');
}
public function preUpdate($supplier)
{
$supplier->setDeleted(0);
return $supplier;
}
public function preCreate($supplier)
{
$supplier->setDeleted(0);
return $supplier;
}
}
I want my show form to display all the postcode entries for the a particular supplier but at the moment it's blank...
Can anyone spot what it is I need to do?
Thanks in advance :)
Nathan
Related
I have a problem, I have two entitys, Contenu(One) and Article(Many), I have make my relation, who it bidirectionnal.
I use FormType imbricate and when I submit my form, I have an array for my entity Article instead an object, and I can't persist with an array.
If you have any solution, thanks
Entity Contenu
<?php
/**
* Contenu
*
* #ORM\Table(name="ge_contenu")
* #ORM\Entity(repositoryClass="GE\MainBundle\Repository\ContenuRepository")
*/
class Contenu
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="entete", type="text")
*/
private $entete;
/**
* #var string
*
* #ORM\Column(name="texte", type="text")
*/
private $texte;
/**
* #var string
*
* #ORM\Column(name="conclusion", type="text")
*/
private $conclusion;
/**
* #ORM\OneToMany(targetEntity="AdminBundle\Entity\Article", mappedBy="contenu")
*/
private $articles;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set texte
*
* #param string $texte
*
* #return Contenu
*/
public function setTexte($texte)
{
$this->texte = $texte;
return $this;
}
/**
* Get texte
*
* #return string
*/
public function getTexte()
{
return $this->texte;
}
/**
* Set entete
*
* #param string $entete
*
* #return Contenu
*/
public function setEntete($entete)
{
$this->entete = $entete;
return $this;
}
/**
* Get entete
*
* #return string
*/
public function getEntete()
{
return $this->entete;
}
/**
* Set conclusion
*
* #param string $conclusion
*
* #return Contenu
*/
public function setConclusion($conclusion)
{
$this->conclusion = $conclusion;
return $this;
}
/**
* Get conclusion
*
* #return string
*/
public function getConclusion()
{
return $this->conclusion;
}
/**
* Constructor
*/
public function __construct()
{
$this->articles = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add article
*
* #param \AdminBundle\Entity\Article $article
*
* #return Contenu
*/
public function addArticle(\AdminBundle\Entity\Article $article)
{
$this->articles[] = $article;
$article->setContenu($this);
return $this;
}
/**
* Remove article
*
* #param \AdminBundle\Entity\Article $article
*/
public function removeArticle(\AdminBundle\Entity\Article $article)
{
$this->articles->removeElement($article);
}
/**
* Get articles
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getArticles()
{
return $this->articles;
}
}
Entity Article
<?php
/**
* Article
*
* #ORM\Table(name="ge_article")
* #ORM\Entity(repositoryClass="GE\MainBundle\Repository\ArticleRepository")
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=150)
*/
private $titre;
/**
* #var string
*
* #ORM\Column(name="texte", type="text")
*/
private $texte;
/**
* #var Article $article
*
* #ORM\ManyToOne(targetEntity="AdminBundle\Entity\Contenu", inversedBy="articles")
* #ORM\JoinColumn(nullable=false)
*/
public $contenu;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set titre
*
* #param string $titre
*
* #return Article
*/
public function setTitre($titre)
{
$this->titre = $titre;
return $this;
}
/**
* Get titre
*
* #return string
*/
public function getTitre()
{
return $this->titre;
}
/**
* Set texte
*
* #param string $texte
*
* #return Article
*/
public function setTexte($texte)
{
$this->texte = $texte;
return $this;
}
/**
* Get texte
*
* #return string
*/
public function getTexte()
{
return $this->texte;
}
/**
* Set contenu
*
* #param \AdminBundle\Entity\Contenu $contenu
*
* #return Article
*/
public function setContenu(\AdminBundle\Entity\Contenu $contenu)
{
$this->contenu = $contenu;
return $this;
}
/**
* Get contenu
*
* #return \AdminBundle\Entity\Contenu
*/
public function getContenu()
{
return $this->contenu;
}
}
My controller
public function contenuAction($id = null, Request $request)
{
$em = $this
->getDoctrine()
->getManager()
;
$pages_contenu = $em->getRepository('AdminBundle:Page')->findBy(
array("contenu" => true),
array("nom" => "asc"),
null,
null
);
$vars = array(
"modif" => null,
"modif" => null,
"pages_contenu" => $pages_contenu
);
if (isset($id))
{
$contenu = $em->getRepository('AdminBundle:Contenu')->findOneById($id);
if ($contenu == null)
{
$contenu = new Contenu();
$vars["modif"] = $contenu;
}
else
{
$vars["modif"] = $contenu;
}
}
else
{
$contenu = new Contenu();
}
$form = $this->get('form.factory')->create(ContenuType::class, $contenu);
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid())
{
$em->persist($contenu);
$em->flush();
$request->getSession()->getFlashBag()->set('valid', 'Contenu bien enregistré.');
return $this->redirectToRoute('admin_contenu');
}
$vars['form'] = $form->createView();
return $this->render('AdminBundle:Admin:contenu.html.twig', $vars);
}
Contenu Type
class ContenuType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('entete', TextareaType::class)
->add('texte', TextareaType::class)
->add('articles', CollectionType::class, array(
'entry_type' => ArticleType::class,
'allow_add' => true,
'allow_delete' => true
))
->add('conclusion', TextareaType::class)
->add('submit', SubmitType::class, array('label' => 'Enregistrer'))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AdminBundle\Entity\Contenu'
));
}
public function getName()
{
return 'adminbundle_contenu';
}
}
Article Type
class ArticleType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('titre', TextType::class)
->add('texte', TextareaType::class)
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AdminBundle\Entity\Article'
));
}
public function getName()
{
return 'adminbundle_article';
}
}
You have to add the following method to your ArticleType class:
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'data_class' => 'AdminBundle\Entity\ArticleType'
));
}
I encountered a problem under symfony3 which has blocked me for a while ... I do not understand this error:
Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\Project#$participants", got "string" instead.
Here is my entity Project:
class Project
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="projects")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Participant", mappedBy="project", cascade={"persist", "remove"})
*/
private $participants;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="content", type="text", nullable=true)
*/
private $content;
/**
* #var string
*
* #ORM\Column(name="place", type="string", length=255, nullable=true)
*/
private $place;
/**
* #var \DateTime
*
* #ORM\Column(name="dateEvent", type="datetime", nullable=true)
*/
private $dateEvent;
/**
* #var boolean
*
* #ORM\Column(name="status", type="boolean")
*/
private $status;
/**
* #var boolean
*
* #ORM\Column(name="in_progress", type="boolean")
*/
private $inProgress;
/**
* #var boolean
*
* #ORM\Column(name="accept_list", type="boolean")
*/
private $acceptList;
/**
* #var boolean
*
* #ORM\Column(name="visible_list", type="boolean")
*/
private $visibleList;
/**
* #var boolean
*
* #ORM\Column(name="many_loop", type="boolean")
*/
private $manyLoop;
/**
* #var string
*
* #ORM\Column(name="text_email", type="text", nullable=true)
*/
private $textEmail;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
public function __construct()
{
$this->run = false;
$this->status = false;
$this->acceptList = true;
$this->visibleList = true;
$this->inProgress = false;
$this->manyLoop = true;
$this->created = new \Datetime('now');
$this->participants = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Project
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*
* #return Project
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set place
*
* #param string $place
*
* #return Project
*/
public function setPlace($place)
{
$this->place = $place;
return $this;
}
/**
* Get place
*
* #return string
*/
public function getPlace()
{
return $this->place;
}
/**
* Set dateEvent
*
* #param \DateTime $dateEvent
*
* #return Project
*/
public function setDateEvent($dateEvent)
{
$this->dateEvent = $dateEvent;
return $this;
}
/**
* Get dateEvent
*
* #return \DateTime
*/
public function getDateEvent()
{
return $this->dateEvent;
}
/**
* Set status
*
* #param boolean $status
*
* #return Project
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* #return boolean
*/
public function getStatus()
{
return $this->status;
}
/**
* Set inProgress
*
* #param boolean $inProgress
*
* #return Project
*/
public function setInProgress($inProgress)
{
$this->inProgress = $inProgress;
return $this;
}
/**
* Get inProgress
*
* #return boolean
*/
public function getInProgress()
{
return $this->inProgress;
}
/**
* Set acceptList
*
* #param boolean $acceptList
*
* #return Project
*/
public function setAcceptList($acceptList)
{
$this->acceptList = $acceptList;
return $this;
}
/**
* Get acceptList
*
* #return boolean
*/
public function getAcceptList()
{
return $this->acceptList;
}
/**
* Set visibleList
*
* #param boolean $visibleList
*
* #return Project
*/
public function setVisibleList($visibleList)
{
$this->visibleList = $visibleList;
return $this;
}
/**
* Get visibleList
*
* #return boolean
*/
public function getVisibleList()
{
return $this->visibleList;
}
/**
* Set manyLoop
*
* #param boolean $manyLoop
*
* #return Project
*/
public function setManyLoop($manyLoop)
{
$this->manyLoop = $manyLoop;
return $this;
}
/**
* Get manyLoop
*
* #return boolean
*/
public function getManyLoop()
{
return $this->manyLoop;
}
/**
* Set textEmail
*
* #param string $textEmail
*
* #return Project
*/
public function setTextEmail($textEmail)
{
$this->textEmail = $textEmail;
return $this;
}
/**
* Get textEmail
*
* #return string
*/
public function getTextEmail()
{
return $this->textEmail;
}
/**
* Set created
*
* #param \DateTime $created
*
* #return Project
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set user
*
* #param \AppBundle\Entity\User $user
*
* #return Project
*/
public function setUser(\AppBundle\Entity\User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Add participant
*
* #param \AppBundle\Entity\Participant $participant
*
* #return Project
*/
public function addParticipant(\AppBundle\Entity\Participant $participant)
{
$this->participants[] = $participant;
// On lie l'annonce à la candidature
$participant->setProject($this);
return $this;
}
/**
* Remove participant
*
* #param \AppBundle\Entity\Participant $participant
*/
public function removeParticipant(\AppBundle\Entity\Participant $participant)
{
$this->participants->removeElement($participant);
}
/**
* Get participants
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getParticipants()
{
return $this->participants;
}}
My Form:
$builder ->add('participants', ParticipantType::class);
/**
* {#inheritdoc}
*/
public function configureOptions (OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Project',
'step' => 1,
));
}
Form imbricated :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('lastName', TextType::class, array(
'required' => true,
'label' => 'Nom'
))
->add('firstName', TextType::class, array(
'required' => true,
'label' => 'Prénom'
))
->add('email', EmailType::class, array(
'required' => true,
'label' => 'Adresse mail'
));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => array('registration')
));
}
So here is to summarize my problem, I do not understand the error that Symfony returns, yet from the examples on the internet I do not see why there is a problem here ...
If someone to a solution would be top, because I blocked on this problem for a long time ...
Thank's !
Make sure that you don't forget to add : use Doctrine\Common\Collections\ArrayCollection or use $this->participants = new \Doctrine\Common\Collections\ArrayCollection();
In your FormType you should configure the participants field as a collection of Participant. It should be done using CollectionType like this:
$builder->add('participants', CollectionType::class, array(
'entry_type' => ParticipantType::class,
'allow_add' => true,
...
));
EDIT
According to your data model, the participants Field is a collection of Participant object. As you can see in the error message you presented, an array is expected. The CollectionType will always return an array of the target type.
That is why I suggest you to consider using a CollectionType in your form type. You can learn more about CollectionType here and about Symfony forms in general here
I make the assumption when an entity has a OneToMany relationship with another, the children entities will be populated when querying the parent entity.
$account = $this->Repository()->findById($organization, $id, $accountList);
print_r($account->getAttributes()); //I would expect the children to be populated/returned when making this call
I would expect that the AccountAttributes would be returned for the Account but $account->getAttributes() returns null.
Any Ideas?
Account Entity
<?php
namespace Application\AccountBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Application\AccountBundle\Entity\AccountAttribute;
/**
* Account
*
* #ORM\Table(name="accounts")
* #ORM\Entity(repositoryClass="Application\AccountBundle\Repository\AccountRepository")
*/
class Account
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="display_name", type="string", length=255)
*/
private $displayName;
/**
* #var \Application\OrganizationBundle\Entity\Organization
*
* #ORM\ManyToOne(targetEntity="Application\OrganizationBundle\Entity\Organization")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="organization_id", referencedColumnName="id")
* })
*/
private $organization;
/**
* #var boolean
*
* #ORM\Column(name="active", type="boolean")
*/
private $active;
/**
* #var \DateTime
*
* #ORM\Column(name="deactivated", type="datetime")
*/
private $deactivated;
/**
* #var boolean
*
* #ORM\Column(name="deleted", type="boolean")
*/
private $deleted;
/**
* #var \DateTime
*
* #ORM\Column(name="updated", type="datetime")
*/
private $updated;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
/**
*
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="AccountAttribute", mappedBy="account", cascade={"ALL"}, indexBy="attribute")
*
*/
private $attributes;
public function __construct()
{
$this->attributes = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set displayName
*
* #param string $displayName
* #return Account
*/
public function setDisplayName($displayName)
{
$this->displayName = $displayName;
return $this;
}
/**
* Get displayName
*
* #return string
*/
public function getDisplayName()
{
return $this->displayName;
}
/**
* Set active
*
* #param boolean $active
* #return Account
*/
public function setActive($active)
{
$this->active = $active;
return $this;
}
/**
* Get active
*
* #return boolean
*/
public function getActive()
{
return $this->active;
}
/**
* Set deactivated
*
* #param \DateTime $deactivated
* #return Account
*/
public function setDeactivated($deactivated)
{
$this->deactivated = $deactivated;
return $this;
}
/**
* Get deactivated
*
* #return \DateTime
*/
public function getDeactivated()
{
return $this->deactivated;
}
/**
* Set deleted
*
* #param boolean $deleted
* #return Account
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
/**
* Get deleted
*
* #return boolean
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* Set updated
*
* #param \DateTime $updated
* #return Account
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* #return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set created
*
* #param \DateTime $created
* #return Account
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
public function getOrganization()
{
return $this->organization;
}
public function getAttributes()
{
return $this->attributes;
}
public function setAttributes($attributes)
{
foreach($attributes as $item)
{
$this->addAttribute($item);
}
return $this;
}
public function addAttribute(AccountAttribute $attribute)
{
$this->attributes->add($attribute);
return $this;
}
public function getAttributeCollection()
{
$data = array();
$attributes = $this->getAttributes();
foreach ($attributes as $item)
{
$data[$item->getDataKey()] = $item->getDataValue();
}
return $data;
}
}
Account Attribute Entity
<?php
namespace Application\AccountBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Application\AccountBundle\Entity\Account;
use Application\AccountBundle\Entity\AccountAttribute;
use Application\OrganizationBundle\Entity\Organization;
/**
* Account
*
* #ORM\Table(name="accountattribute")
* #ORM\Entity(repositoryClass="Application\AccountBundle\Repository\AccountAttributeRepository")
*/
class AccountAttribute
{
/**
* #var \Application\AccountBundle\Entity\Account
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Account", inversedBy="attributes")
* #ORM\JoinColumn(name="account_id", referencedColumnName="id")
*/
private $account;
/**
* #var \Application\OrganizationBundle\Entity\Organization
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Application\OrganizationBundle\Entity\Organization")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="organization_id", referencedColumnName="id")
* })
*/
private $organization;
/**
* #var string
* #ORM\Id
* #ORM\Column(type="string")
*/
private $dataKey;
/**
* #var text
* #ORM\Column(type="text")
*/
private $dataValue;
/**
* #var \DateTime
* #ORM\Column(name="updated", type="datetime")
*/
private $updated;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
public function __construct(Account $account, $organizationId, $key, $value )
{
$this->account = $account;
$this->organizationId = $organizationId;
$this->dataKey = $key;
$this->dataValue = $key;
}
public function getKey()
{
return $this->dataKey;
}
public function setKey($key)
{
$this->dataKey = $key;
return $this;
}
public function getValue()
{
return $this->dataValue;
}
public function setValue($value)
{
$this->dataValue = $value;
return $this;
}
public function getAccount()
{
return $this->account;
}
public function setAccount(Account $account)
{
$this->account = $account;
return $this;
}
}
Account Repository
<?php
namespace Application\AccountBundle\Repository;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Query;
use Application\AccountBundle\Entity\Account;
/**
* AccountRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class AccountRepository extends EntityRepository
{
public function findById($organization_id, $id, $accountList)
{
if( empty( $accountList ) )
return null;
$em = $this->getEntityManager();
$emConfig = $em->getConfiguration();
$emConfig->addCustomDatetimeFunction('YEAR', 'DoctrineExtensions\Query\Mysql\Year');
$emConfig->addCustomDatetimeFunction('MONTH', 'DoctrineExtensions\Query\Mysql\Month');
$emConfig->addCustomDatetimeFunction('DAY', 'DoctrineExtensions\Query\Mysql\Day');
$q = $em
->createQuery(
"SELECT a
FROM ApplicationAccountBundle:Account a
WHERE
a.id IN ( " . implode(',', $accountList ) . ") AND
a.deleted = false AND
a.organization = :organization_id AND
a.id = :id AND
(
a.active = 1 OR
(
a.active = 0 AND
MONTH(a.deactivated) >= :current_month AND
YEAR(a.deactivated) >= :current_year
)
)
"
)
->setParameters(
array(
'id' => $id,
'organization_id' => $organization_id,
'current_month' => date( "n" ),
'current_year' => date( "Y" )
)
);
try {
$account = $q->getOneOrNullResult();
} catch (NoResultException $e) {
}
return $account;
}
public function findAllAccounts($organization_id, $accountList)
{
if( empty( $accountList ) )
return null;
$em = $this->getEntityManager();
$emConfig = $em->getConfiguration();
$emConfig->addCustomDatetimeFunction('YEAR', 'DoctrineExtensions\Query\Mysql\Year');
$emConfig->addCustomDatetimeFunction('MONTH', 'DoctrineExtensions\Query\Mysql\Month');
$emConfig->addCustomDatetimeFunction('DAY', 'DoctrineExtensions\Query\Mysql\Day');
$q = $em
->createQuery(
"SELECT e
FROM ApplicationAccountBundle:Account a
WHERE
a.id IN ( " . implode(',', $accountList ) . ") AND
a.deleted = false AND
a.organization = :organization_id AND
(
a.active = 1 OR
(
a.active = 0 AND
MONTH(a.deactivated) >= :current_month AND
YEAR(a.deactivated) >= :current_year
)
)
"
)
->setParameters(
array(
'organization_id' => $organization_id,
'current_month' => date( "n" ),
'current_year' => date( "Y" )
)
);
try {
$accounts = $q->getResult();
} catch (NoResultException $e) {
}
return $accounts;
}
public function findByACL($userIdentity, $mask, $className)
{
$sql = <<<SELECTCLAUSE
SELECT oid.object_identifier
FROM acl_entries ent
JOIN acl_object_identities oid ON
oid.class_id = ent.class_id
JOIN acl_security_identities s ON
s.id = ent.security_identity_id
JOIN acl_classes cls ON
cls.id = ent.class_id
WHERE
ent.object_identity_id IS NOT NULL AND
(ent.mask & %d) AND
s.identifier = %s AND
cls.class_type = %s
GROUP BY
oid.object_identifier;
SELECTCLAUSE;
$connection = $this->getEntityManager()->getConnection();
$query = sprintf(
$sql,
$mask,
$connection->quote("{$userIdentity->getClass()}-{$userIdentity->getUsername()}"),
$connection->quote($className)
);
//Execute query to get all of the IDs
$ids = $connection->executeQuery($query)->fetchAll(\PDO::FETCH_COLUMN);
return $ids;
}
public function save(Account $account)
{
$em = $this->getEntityManager();
$em->getConnection()->beginTransaction();
$em->persist($account);
try {
$em->flush();
$em->getConnection()->commit();
} catch (\Exception $e) {
$em->getConnection()->rollback();
echo "Unable to save Account";
}
return $account;
}
}
Table Structures
CREATE TABLE `accounts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`organization_id` int(11) NOT NULL,
`display_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`active` tinyint(1) NOT NULL DEFAULT '1',
`deactivated` datetime DEFAULT NULL,
`deleted` tinyint(1) NOT NULL DEFAULT '0',
`updated` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_FA6F25A332C8A3DE` (`organization_id`),
CONSTRAINT `FK_FA6F25A332C8A3DE` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `accountattribute` (
`account_id` int(11) NOT NULL,
`organization_id` int(11) NOT NULL,
`dataKey` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`dataValue` text COLLATE utf8_unicode_ci NOT NULL,
`updated` datetime NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`account_id`,`organization_id`,`dataKey`),
KEY `organization_id` (`organization_id`),
CONSTRAINT `accountattribute_ibfk_2` FOREIGN KEY (`organization_id`) REFERENCES `organizations` (`id`),
CONSTRAINT `accountattribute_ibfk_1` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
here is your problem: in your repository call you only selected a, therefore only the account will get populated.
I don't understand why some constraints does not insert name of property in error message after validation. I have this entity class:
<?php
namespace AC\OperaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use AC\UserBundle\Entity\Utente;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class Episodio
* #package AC\OperaBundle\Entity
* #ORM\Entity(repositoryClass="AC\OperaBundle\Repository\EpisodioRepository")
* * #ORM\Table(
* name="ac_Episodio",
* uniqueConstraints={#ORM\UniqueConstraint(name="unique_idx", columns={"opera", "numero_episodio", "extra"})},
* indexes={ #ORM\Index(name="opera_idx", columns={"opera"}),
* #ORM\Index(name="numero_episodio_idx", columns={"numero_episodio"}),
* #ORM\Index(name="extra_idx", columns={"extra"}),
* #ORM\Index(name="attivo_idx", columns={"attivo"}) })
*/
class Episodio {
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var Opera
*
* #ORM\ManyToOne(targetEntity="AC\OperaBundle\Entity\Opera", inversedBy="episodi")
* #ORM\JoinColumn(name="opera", referencedColumnName="id", nullable=false)
*/
protected $opera;
/**
* #var string
*
* #ORM\Column(name="numero_episodio", type="string", length=5, nullable=false)
*/
protected $numero_episodio;
/**
* #var string
*
* #ORM\Column(name="extra", type="string", length=30, nullable=false)
*/
protected $extra;
/**
* #var string
*
* #ORM\Column(name="titolo_italiano", type="string", length=150, nullable=false)
* #Assert\NotBlank()
*/
protected $titolo_italiano;
/**
* #var string
*
* #ORM\Column(name="titolo_originale", type="string", length=150, nullable=false)
* #Assert\NotBlank()
*/
protected $titolo_originale;
/**
* #var \DateTime
*
* #ORM\Column(name="data_ita", type="date", nullable=true)
* #Assert\Date()
*/
protected $data_ita;
/**
* #var \DateTime
*
* #ORM\Column(name="data_jap", type="date", nullable=true)
* #Assert\Date()
*/
protected $data_jap;
/**
* #var int
*
* #ORM\Column(name="durata", type="smallint", nullable=false, options={"default" = 0})
*/
protected $durata;
/**
* #var string
*
* #ORM\Column(name="trama", type="text", nullable=false)
*/
protected $trama;
/**
* #var int
*
* #ORM\Column(name="ordine", type="smallint", nullable=false, options={"default" = 0})
*/
protected $ordine;
/**
* #var int
*
* #ORM\Column(name="promosso", type="integer", nullable=false, options={"default" = 0})
*/
protected $promosso;
/**
* #var int
*
* #ORM\Column(name="rimandato", type="integer", nullable=false, options={"default" = 0})
*/
protected $rimandato;
/**
* #var int
*
* #ORM\Column(name="bocciato", type="integer", nullable=false, options={"default" = 0})
*/
protected $bocciato;
/**
* #var boolean
*
* #ORM\Column(name="fansub", type="boolean", nullable=false)
*/
protected $fansub;
/**
* #var boolean
*
* #ORM\Column(name="attivo", type="boolean", nullable=false)
*/
protected $attivo;
/**
* #var \DateTime
*
* #ORM\Column(name="data_aggiornamento", type="date")
*/
protected $data_aggiornamento;
/**
* #var Utente
*
* #ORM\ManyToOne(targetEntity="AC\UserBundle\Entity\Utente")
* #ORM\JoinColumn(name="utente_aggiornamento", referencedColumnName="id", nullable=true)
*/
protected $utente_aggiornamento;
/**
* #param boolean $attivo
*/
public function setAttivo($attivo)
{
$this->attivo = $attivo;
}
/**
* #return boolean
*/
public function getAttivo()
{
return $this->attivo;
}
/**
* #param int $bocciato
*/
public function setBocciato($bocciato)
{
$this->bocciato = $bocciato;
}
/**
* #return int
*/
public function getBocciato()
{
return $this->bocciato;
}
/**
* #param \DateTime $data_aggiornamento
*/
public function setDataAggiornamento($data_aggiornamento)
{
$this->data_aggiornamento = $data_aggiornamento;
}
/**
* #return \DateTime
*/
public function getDataAggiornamento()
{
return $this->data_aggiornamento;
}
/**
* #param \DateTime $data_ita
*/
public function setDataIta($data_ita)
{
$this->data_ita = $data_ita;
}
/**
* #return \DateTime
*/
public function getDataIta()
{
return $this->data_ita;
}
/**
* #param \DateTime $data_jap
*/
public function setDataJap($data_jap)
{
$this->data_jap = $data_jap;
}
/**
* #return \DateTime
*/
public function getDataJap()
{
return $this->data_jap;
}
/**
* #param int $durata
*/
public function setDurata($durata)
{
$this->durata = $durata;
}
/**
* #return int
*/
public function getDurata()
{
return $this->durata;
}
/**
* #param string $extra
*/
public function setExtra($extra)
{
$this->extra = $extra;
}
/**
* #return string
*/
public function getExtra()
{
return $this->extra;
}
/**
* #param boolean $fansub
*/
public function setFansub($fansub)
{
$this->fansub = $fansub;
}
/**
* #return boolean
*/
public function getFansub()
{
return $this->fansub;
}
/**
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param string $numero_episodio
*/
public function setNumeroEpisodio($numero_episodio)
{
$this->numero_episodio = $numero_episodio;
}
/**
* #return string
*/
public function getNumeroEpisodio()
{
return $this->numero_episodio;
}
/**
* #param \AC\OperaBundle\Entity\Opera $opera
*/
public function setOpera($opera)
{
$this->opera = $opera;
}
/**
* #return \AC\OperaBundle\Entity\Opera
*/
public function getOpera()
{
return $this->opera;
}
/**
* #param int $ordine
*/
public function setOrdine($ordine)
{
$this->ordine = $ordine;
}
/**
* #return int
*/
public function getOrdine()
{
return $this->ordine;
}
/**
* #param int $promosso
*/
public function setPromosso($promosso)
{
$this->promosso = $promosso;
}
/**
* #return int
*/
public function getPromosso()
{
return $this->promosso;
}
/**
* #param int $rimandato
*/
public function setRimandato($rimandato)
{
$this->rimandato = $rimandato;
}
/**
* #return int
*/
public function getRimandato()
{
return $this->rimandato;
}
/**
* #param string $titolo_italiano
*/
public function setTitoloItaliano($titolo_italiano)
{
$this->titolo_italiano = $titolo_italiano;
}
/**
* #return string
*/
public function getTitoloItaliano()
{
return $this->titolo_italiano;
}
/**
* #param string $titolo_originale
*/
public function setTitoloOriginale($titolo_originale)
{
$this->titolo_originale = $titolo_originale;
}
/**
* #return string
*/
public function getTitoloOriginale()
{
return $this->titolo_originale;
}
/**
* #param string $trama
*/
public function setTrama($trama)
{
$this->trama = $trama;
}
/**
* #return string
*/
public function getTrama()
{
return $this->trama;
}
/**
* #param \AC\UserBundle\Entity\Utente $utente_aggiornamento
*/
public function setUtenteAggiornamento($utente_aggiornamento)
{
$this->utente_aggiornamento = $utente_aggiornamento;
}
/**
* #return \AC\UserBundle\Entity\Utente
*/
public function getUtenteAggiornamento()
{
return $this->utente_aggiornamento;
}
}
In the controller perform the classi call at the $form->isValid() method to check validation. If there are errors i call $form->getErrorsAsString() and this is the result:
ERROR: This value should not be blank.
ERROR: This value should not be blank.
numeroEpisodio:
No errors
titoloItaliano:
No errors
titoloOriginale:
No errors
dataIta:
ERROR: This value is not valid.
dataJap:
ERROR: This value is not valid.
durata:
No errors
trama:
No errors
extra:
No errors
fansub:
No errors
The property that use #Assert\NotBlank() dose not put property name in error message! And for this reason i have the first two error line with e generic:
ERROR: This value should not be blank.
ERROR: This value should not be blank.
I i don't know who is the property that failed the validation. I look in the source code of Symfony Component and i see that for not blank constraint:
/**
* #author Bernhard Schussek <bschussek#gmail.com>
*
* #api
*/
class NotBlankValidator extends ConstraintValidator
{
/**
* {#inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof NotBlank) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\NotBlank');
}
if (false === $value || (empty($value) && '0' != $value)) {
$this->context->addViolation($constraint->message);
}
}
}
And for data constraint:
/**
* #author Bernhard Schussek <bschussek#gmail.com>
*
* #api
*/
class DateValidator extends ConstraintValidator
{
const PATTERN = '/^(\d{4})-(\d{2})-(\d{2})$/';
/**
* {#inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof Date) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Date');
}
if (null === $value || '' === $value || $value instanceof \DateTime) {
return;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) {
throw new UnexpectedTypeException($value, 'string');
}
$value = (string) $value;
if (!preg_match(static::PATTERN, $value, $matches) || !checkdate($matches[2], $matches[3], $matches[1])) {
$this->context->addViolation($constraint->message, array('{{ value }}' => $value));
}
}
}
The important difference is $this->context->addViolation($constraint->message); VS $this->context->addViolation($constraint->message, array('{{ value }}' => $value));
Why?
This is core part of controller
if ($request->getMethod() == 'POST') {
try {
$form->handleRequest($request);
if ($form->isValid()) {
/* #var $item Episodio */
$item = $form->getData();
$em->persist($item);
$em->flush();
$msg = new Message(true, Message::OK_MESSAGE);
} else {
$msg = new Message(false, Message::KO_MESSAGE);
$errors = Utility::getErrorMessages($form);
$msg->setData($errors);
}
} catch (\Exception $ex) {
$msg = new Message(false, $ex->getMessage());
}
return new Response($this->get('jms_serializer')->serialize($msg, 'json'));
}
This is utility class that fetch error from form
class Utility {
static function getErrorMessages(\Symfony\Component\Form\Form $form) {
$errors = array();
foreach ($form->getErrors() as $key => $error) {
$template = $error->getMessageTemplate();
$parameters = $error->getMessageParameters();
foreach($parameters as $var => $value){
$template = str_replace($var, $value, $template);
}
$errors[$key] = $template;
}
//if ($form->hasChildren()) {
foreach ($form->all() as $child) {
if (!$child->isValid()) {
$errors[$child->getName()] = Utility::getErrorMessages($child);
}
}
//}
return $errors;
}
}
Form Class
class EpisodioForm extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('numeroEpisodio', 'text');
$builder->add('titoloItaliano', 'text',array());
$builder->add('titoloOriginale', 'text');
$builder->add('dataIta', 'date', array('widget' => 'single_text', 'format' => 'dd/MM/yyyy'));
$builder->add('dataJap', 'date', array('widget' => 'single_text', 'format' => 'dd/MM/yyyy'));
$builder->add('durata', 'text');
$builder->add('trama', 'textarea');
$builder->add('extra', 'text');
$builder->add('fansub', 'checkbox');
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
public function getName()
{
return "episodio_form";
}
}
The version of framework is Symfony 2.4
Why $this->context->addViolation($constraint->message); VS $this->context->addViolation($constraint->message, array('{{ value }}' => $value));?
This is because the date constraint validator shows the value which failed to validate in the error message, e.g.
45.04.2014 is not a valid date.
whereas the NotBlank constraint validator doesn't need to, since the value causing the error is always empty.
Putting the property name in the error message
This could be achieved:
by changing the annotations for the constraints in your entity to something like:
class Episodio {
/**
* #Assert\NotBlank(message="'titolo_italiano' should not be blank.")
*/
protected $titolo_italiano;
/**
* #Assert\NotBlank(message="'titolo_originale' should not be blank.")
*/
protected $titolo_originale;
}
I left out the column definitions and the other fields in the entity for the sake of readability.
OR by defining a custom validator which passes the name of the property to the error message:
The constraint:
class MyCustomNotBlank extends NotBlank
{
public $message = "'{{ propertyName }}' should not be blank.";
}
The validator:
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class MyCustomNotBlankValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if (!$value) {
$this->context->addViolation(
$constraint->message,
array('{{ propertyName}}' => $this->context->getPropertyPath())
);
}
}
}
This shows you how to define a custom validation constraint:
http://symfony.com/doc/current/cookbook/validation/custom_constraint.html
Obtaining a key-value array with property name and error message
Afaik, there is no function in Symfony2's form component which does that, so it has to be implemented.
See https://stackoverflow.com/a/13763053/277106 . You can add that function to a service for reusability. In your (AJAX) controller you can then do:
public function someAction(Request $request)
{
$errors = array();
$form = $this->createForm(new MyType);
if (!$form->bindRequest($request)->isValid()) {
$errors = $this->myFormService->getErrorMessages($form);
}
else {
// save the entity
}
return JsonResponse($errors);
}
I have this entity (Registro):
<?php
namespace Gitek\RegistroBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Gitek\RegistroBundle\Entity\Registro
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Gitek\RegistroBundle\Entity\RegistroRepository"))
*/
class Registro
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var datetime $fecha
*
* #ORM\Column(name="fecha", type="datetime")
*/
private $fecha;
/**
* #var smallint $comenzado
*
* #ORM\Column(name="comenzado", type="smallint", nullable=true)
*/
private $comenzado;
/**
* #var smallint $completado
*
* #ORM\Column(name="completado", type="smallint", nullable=true)
*/
private $completado;
/**
* #var datetime $created_at
*
* #ORM\Column(name="created_at", type="datetime")
*/
private $created_at;
/**
* #var datetime $updated_at
*
* #ORM\Column(name="updated_at", type="datetime")
*/
private $updated_at;
/** #ORM\ManyToOne(targetEntity="Gitek\UsuarioBundle\Entity\Usuario") */
protected $usuario;
/** #ORM\ManyToOne(targetEntity="Gitek\HotelBundle\Entity\Tipotarea") */
protected $tipotarea;
/** #ORM\ManyToOne(targetEntity="Gitek\HotelBundle\Entity\Habitacion") */
protected $habitacion;
/** #ORM\ManyToOne(targetEntity="Gitek\RegistroBundle\Entity\Master") */
protected $master;
/**
* #ORM\ManyToMany(targetEntity="Gitek\HotelBundle\Entity\Incidencia", inversedBy="registros")
* #ORM\JoinTable(name="incidencia_registro",
* joinColumns={#ORM\JoinColumn(name="registro_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="incidencia_id", referencedColumnName="id")}
* )
*/
protected $incidencias;
public function __construct()
{
$this->created_at = new \DateTime();
$this->updated_at = new \DateTime();
}
// public function __toString()
// {
// return $this->getNombre();
// }
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set fecha
*
* #param datetime $fecha
*/
public function setFecha($fecha)
{
$this->fecha = $fecha;
}
/**
* Get fecha
*
* #return datetime
*/
public function getFecha()
{
return $this->fecha;
}
/**
* Set comenzado
*
* #param smallint $comenzado
*/
public function setComenzado($comenzado)
{
$this->comenzado = $comenzado;
}
/**
* Get comenzado
*
* #return smallint
*/
public function getComenzado()
{
return $this->comenzado;
}
/**
* Set completado
*
* #param smallint $completado
*/
public function setCompletado($completado)
{
$this->completado = $completado;
}
/**
* Get completado
*
* #return smallint
*/
public function getCompletado()
{
return $this->completado;
}
/**
* Set created_at
*
* #param datetime $createdAt
*/
public function setCreatedAt($createdAt)
{
$this->created_at = $createdAt;
}
/**
* Get created_at
*
* #return datetime
*/
public function getCreatedAt()
{
return $this->created_at;
}
/**
* Set updated_at
*
* #param datetime $updatedAt
*/
public function setUpdatedAt($updatedAt)
{
$this->updated_at = $updatedAt;
}
/**
* Get updated_at
*
* #return datetime
*/
public function getUpdatedAt()
{
return $this->updated_at;
}
/**
* Set usuario
*
* #param Gitek\UsuarioBundle\Entity\Usuario $usuario
*/
public function setUsuario(\Gitek\UsuarioBundle\Entity\Usuario $usuario)
{
$this->usuario = $usuario;
}
/**
* Get usuario
*
* #return Gitek\UsuarioBundle\Entity\Usuario
*/
public function getUsuario()
{
return $this->usuario;
}
/**
* Set tipotarea
*
* #param Gitek\HotelBundle\Entity\Tipotarea $tipotarea
*/
public function setTipotarea(\Gitek\HotelBundle\Entity\Tipotarea $tipotarea)
{
$this->tipotarea = $tipotarea;
}
/**
* Get tipotarea
*
* #return Gitek\HotelBundle\Entity\Tipotarea
*/
public function getTipotarea()
{
return $this->tipotarea;
}
/**
* Set habitacion
*
* #param Gitek\HotelBundle\Entity\Habitacion $habitacion
*/
public function setHabitacion(\Gitek\HotelBundle\Entity\Habitacion $habitacion)
{
$this->habitacion = $habitacion;
}
/**
* Get habitacion
*
* #return Gitek\HotelBundle\Entity\Habitacion
*/
public function getHabitacion()
{
return $this->habitacion;
}
/**
* Add incidencias
*
* #param Gitek\HotelBundle\Entity\Incidencia $incidencias
*/
public function addIncidencia(\Gitek\HotelBundle\Entity\Incidencia $incidencias)
{
$this->incidencias[] = $incidencias;
}
/**
* Get incidencias
*
* #return Doctrine\Common\Collections\Collection
*/
public function getIncidencias()
{
return $this->incidencias;
}
}
I wanted to save multiple data on a row, so I created a new Entity with only an array propery like this (Master):
<?php
namespace Gitek\RegistroBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
class Master
{
/** #ORM\OneToMany(targetEntity="Gitek\HotelBundle\Entity\Habitacion", mappedBy="master") */
protected $registros;
public function __construct()
{
$this->registros = new ArrayCollection();
}
public function getRegistros()
{
return $this->registros;
}
public function setRegistros(ArrayCollection $registros)
{
$this->registros = $registros;
}
}
I created my MasterType like this:
<?php
namespace Gitek\RegistroBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Doctrine\ORM\EntityRepository;
class MasterType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('registros', 'collection', array(
'type' => new RegistroType(),
'allow_add' => true,
'by_reference' => true,
));
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Gitek\RegistroBundle\Entity\Master'
);
}
public function getName()
{
return 'master';
}
}
And this is my controller:
public function asignarAction()
{
$master = new Master();
$request = $this->getRequest();
$form = $this->createForm(new MasterType(), $master);
if ('POST' === $request->getMethod()) {
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($master);
$em->flush();
return $this->redirect($this->generateUrl('recepcion_asignar'));
}else {
print_r("ezez");
print_r($form->getErrors());
}
} else {
}
return $this->render('RegistroBundle:Recepcion:asignar.html.twig', array(
'registro' => $master,
'form' => $form->createView()
));
}
The form works ok, and I see the data is submited correctly but it is not persisted, I´m getting this error all the time:
Class Gitek\RegistroBundle\Entity\Master is not a valid entity or mapped super class.
I think that the problem is within de Master entity.
Any help or clue?
You missed the #Entity annotation on your Master class, you will also need a master table on your database for this to work.
If you don't want to create the master table, then you can skip the #Entity annotation, but you can not persist master. Instead you would have to iterate through the collection and persist only the entities in the array.