Symfony2 relationship between two entities - php

I want to make a relationship between two entities but for some reason I am getting NULL on one of the values...
So in my skin entity I have an email_registration_id field with the relationship :
/**
* #ORM\ManyToOne(targetEntity="Project\UserBundle\Entity\Email")
* #ORM\JoinColumn(name="email_registration_id", referencedColumnName="id")
*/
protected $email_registration_id;
/**
* Set email_registration_id
*
* #param \Project\UserBundle\Entity\Email $email_registration_id
* #return Skin
*/
public function setEmailRegistrationId(\Project\UserBundle\Entity\Email $email_registration_id = null)
{
$this->email_registration_id = $email_registration_id;
return $this;
}
/**
* Get email_registration_id
*
* #return \Project\UserBundle\Entity\Email
*/
public function getEmailRegistrationId()
{
return $this->email_registration_id;
}
And this is the Email entity:
/**
* Project\UserBundle\Entity\Email
*
* #ORM\Entity
* #ORM\Table(name="email")
*/
class Email {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string $title
*
* #ORM\Column(name="title", type="string")
*/
protected $title;
/**
* #var string $registration_content
*
* #ORM\Column(name="registration_content", type="string")
*/
protected $registration_content;
/**
* #var string $confirmation_apuestas
*
* #ORM\Column(name="confirmation_apuestas", type="string")
*/
protected $confirmation_apuestas;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function setId() {
$this->id = null;
}
/**
* Set title
*
* #param string $title
* #return Email
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set registration_content
*
* #param string $registration_content
* #return Email
*/
public function setRegistrationContent($registration_content)
{
$this->registration_content = $registration_content;
return $this;
}
/**
* Get registration_content
*
* #return string
*/
public function getRegistrationContent()
{
return $this->registration_content;
}
/**
* Set $confirmation_apuestas
*
* #param string $confirmation_apuestas
* #return Email
*/
public function setConfirmationApuestas($confirmation_apuestas)
{
$this->confirmation_apuestas = $confirmation_apuestas;
return $this;
}
/**
* Get $confirmation_apuestas
*
*
#return string
*/
public function getConfirmationApuestas()
{
return $this->confirmation_apuestas;
}
}
Now in my cms i create a new email like this:
/**
* #Route("/new", name="cms_email_new")
* #Method({"GET"})
* #Template()
*/
public function newAction() {
$item = new Email();
$form = $this->createForm(new EmailType($this->container->getParameter("langs")), $item);
return array('form' => $form->createView(), 'item' => $item);
}
The form:
public function buildForm(FormBuilderInterface $builder, array $option) {
$builder->add('title', 'text', array('label' => 'cms.Title'));
$builder->add('registration_content', 'textarea', array('label' => 'cms.registration.content'));
$builder->add('confirmation_apuestas', 'textarea', array('label' => 'cms.confirmation.apuestas'));
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'Project\UserBundle\Entity\Email',
);
}
This is how I persist the Email to the database:
/**
* #Route("/save", name="cms_email_save")
* #Template("ProjectUserBundle:EmailAdmin:new.html.twig")
* #Method({"POST"})
*/
public function saveAction(Request $request) {
$item = new Email();
$type = new EmailType($this->container->getParameter("langs"));
$form = $this->createForm($type, $item);
$form->bind($request);
$em = $this->getEntityManager();
if ($form->isValid()) {
$this->upload($form, $item);
$em->persist($item);
$em->flush();
return $this->redirect($this->generateUrl('cms_skin_email_list', array('skin_id' => $item->getId())));
}
return array('form' => $form->createView(), 'item' => $item);
}
The problem is that it creates an email, but in my skin entity, the email_registration_id is just NULL and not the same as emails Id... Maybe I missed something?
UPDATE
So this is what I made according to the responses I got:
class Email {
/**
* #ORM\OneToMany(targetEntity="Project\SkinBundle\Entity\Skin", mappedBy="email_registration_id")
*/
protected $skin;
/**
* Set skin
*
* #param string $skin
* #return Email
*/
public function setSkin(\Project\SkinBundle\Entity\Skin $skin = null)
{
$this->skin = $skin;
return $this;
}
/**
* Get skin
*
* #return string
*/
public function getSkin()
{
return $this->skin;
}
And the form:
public function buildForm(FormBuilderInterface $builder, array $option) {
$builder->add('title', 'text', array('label' => 'cms.Title'));
$builder->add('registration_content', 'textarea', array('label' => 'cms.registration.content'));
$builder->add('confirmation_apuestas', 'textarea', array('label' => 'cms.confirmation.apuestas'));
$builder->add('skin', 'entity', array(
'class' => 'ProjectSkinBundle:Skin',
'property' => 'id',
));
}
However when i am trying to flush to the database I get this error:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be of the type array, object given, called in C:\wamp\www\Company\front\vendor\doctrine\orm\lib\Doctrine\ORM\UnitOfWork.php on line 528 and defined in C:\wamp\www\Company\front\vendor\doctrine\collections\lib\Doctrine\Common\Collections\ArrayCollection.php line 48

It's normal that the creation of a new email is not associated to a skin object. How could it ? The relation is unidirectional, and only works when you create a skin and you give it an email. To create an email and give it a skin, you have to make the relation bidirectional, and add it to your email form.
To do this, I'd do the following :
/*
* Class Project\UserBundle\Entity\Email
*/
class Email {
...
/**
* #ORM\OneToMany(targetEntity="Project\SkinBundle\Entity\Skin", mappedBy="email_registration_id")
*/
protected $skin;
and in your form :
public function buildForm(FormBuilderInterface $builder, array $option) {
$builder->add('title', 'text', array('label' => 'cms.Title'))
->add('registration_content', 'textarea', array('label' => 'cms.registration.content'))
->add('confirmation_apuestas', 'textarea', array('label' => 'cms.confirmation.apuestas'))
->add('skin')
;
}
And PS : You should not use the bind() method to handle the request, it is deprecated. Instead use :
$form->handleRequest($request);
UPDATE 1 : Don't forget to also update your ManyToOne annotation adding the inversedBy part :
#ORM\ManyToOne(targetEntity="Project\UserBundle\Entity\Email", inversedBy="skin")
And also, of course, update your database.

Related

Create form from variable classnames with parameters

I have a controller ment to give the user the correct form type using variable classnames.
There is a entity named EntityForm which stores data of all forms avaible, with methods to give class name and form type names:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="entity_form")
*/
class EntityForm
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #var string
*
* #ORM\Column(length=12)
*/
protected $url;
/**
* #var string
*
* #ORM\Column
*/
protected $name;
/**
* #var string
*
* #ORM\Column(name="class_name")
*/
protected $className;
/**
* #var string
*
* #ORM\Column(name="description")
*/
protected $description;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set url
*
* #param string $url
*
* #return Form
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* Get url
*
* #return string
*/
public function getUrl()
{
return $this->url;
}
/**
* Set name
*
* #param string $name
*
* #return Form
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set className
*
* #param string $className
*
* #return Form
*/
public function setClassName($className)
{
$this->className = $className;
return $this;
}
/**
* Get className
*
* #return string
*/
public function getClassName()
{
return $this->className;
}
public function createEntity()
{
$entity = sprintf('%s\%s', __NAMESPACE__, $this->getClassName());
return new $entity;
}
public function createType()
{
return sprintf('AppBundle\Form\%sType', $this->getClassName());
}
/**
* Set description
*
* #param string $description
*
* #return Form
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
}
The action mend to create proper forms looks like this:
/**
* #Route("/form/ent/{url}", name="entity_form")
* #Method("GET")
*/
public function formAction(EntityForm $entityForm)
{
$form = $this->createForm($entityForm->createType())
->add('Dodaj!', SubmitType::class, array(
'attr' => array(
'class' => 'btn-info'
)
))
;
return $this->render('default/form.html.twig', [
'form_name' => $entityForm->getName(),
'form_description' => $entityForm->getDescription(),
'form' => $form->createView(),
]);
}
This controller links to a proper form type ie. for the cyclicNewsletter
class CyclicNewsletterType extends AbstractType
{
private $doctrine;
public function __construct($doctrine)
{
$this->doctrine = $doctrine;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$currencies = $this->doctrine->getRepository('AppBundle:Currency')->findAll();
$currenciesFormat = array();
foreach($currencies as $currency){
$currenciesFormat += array($currency->getName() .' ('. $currency->getShortName() . ')' => $currency);
}
$cycles = $this->doctrine->getRepository('AppBundle:Cycle')->findAll();
$cyclesFormat = array();
foreach($cycles as $cycle){
$cyclesFormat += array($cycle->getName() => $cycle);
}
$builder
->add('currency', ChoiceType::class, array(
'label' => 'Waluta',
'choices' => $currenciesFormat,
))
->add('cycle', ChoiceType::class, array(
'label' => 'Cykl',
'choices' => $cyclesFormat,
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => CyclicNewsletter::class,
));
}
}
Now with a simple POST action i can eaisly add new values to the database without writing separate actions for all forms:
/**
* #Route("/form/ent/{url}", name="form_entity_post")
* #Method("POST")
*
*/
public function formPostAction(EntityForm $entityForm, Request $request)
{
$em = $this->getDoctrine()->getManager();
$form = $this->createForm($entityForm->createType())
->add('Dodaj!', SubmitType::class)
;
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$formData = $form->getData();
$formData->setUser($this->container->get('security.token_storage')->getToken()->getUser());
$em->persist($formData);
$em->flush();
return $this->redirectToRoute('form_complete', ['url' => $entityForm->getUrl(), 'entityId' => $formData->getId()]);
}
var_dump('problem ' . $form->getErrors());
exit;
}
However
I cant seem to get the method to work for injecting an already created object into the form builder for editing.
What i mean is write a form create with variables classnames that would also store one instance of entity, which values will be already set in the form.
Is such way possible in symfony?
You will need to add the id of the entity you are editing into your url, and make it optional.
* #Route("/form/ent/{url}/{id}", name="form_entity_post", defaults={"id" = null})
In your controller, when you are building the form you need to get that object from the database and pass it to createForm method as the second argument.
/**
* #Route("/form/ent/{url}/{id}", name="form_entity_post", defaults={"id" = null})
* #Method("POST")
*
*/
public function formPostAction(EntityForm $entityForm, Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = null;
if ($id) {
$entity = $em->getRepository($entityForm->getClassName)->find($id);
}
$form = $this->createForm($entityForm->createType(), $entity)
->add('Dodaj!', SubmitType::class)
;

Symfony form choice default value from entity

I having some troubles with symfony forms.
I have a boolean variable in my entity. I cant find a way to set that value in my form when i'm editing it.
Entity
namespace AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="pages")
*/
class Page
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="text")
*/
protected $slug;
/**
* #ORM\Column(type="text")
*/
protected $title;
/**
* #ORM\Column(type="text")
*/
protected $content;
/**
* #ORM\Column(name="creation_date", type="datetime")
*/
protected $creationDate;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
protected $isActive = true;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set slug
*
* #param string $slug
*
* #return Page
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Set title
*
* #param string $title
*
* #return Page
*/
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 Page
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set creationDate
*
* #param \DateTime $creationDate
*
* #return Page
*/
public function setCreationDate($creationDate)
{
$this->creationDate = $creationDate;
return $this;
}
/**
* Get creationDate
*
* #return \DateTime
*/
public function getCreationDate()
{
return $this->creationDate;
}
/**
* Set isActive
*
* #param boolean $isActive
*
* #return Page
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
}
Controller function
public function editAction(Request $request, $pageId)
{
$em = $this->getDoctrine()->getManager();
$page = $em->getRepository('AdminBundle:Page')->find($pageId);
$form = $this->createForm('app_page', $page);
$form->handleRequest($request);
if($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($page);
$em->flush();
return $this->redirectToRoute('admin_pages');
}
return $this->render('AdminBundle::editPage.html.twig', array('page' => $page, 'form' => $form->createView()));
}
PageFormType
namespace AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class PageFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title', 'text');
$builder->add('slug', 'text');
$builder->add('isActive', 'choice', array('choices' => array(
'true' => 'Active',
'false' => 'Inactive'
)));
$builder->add('content', 'textarea', array('required' => false));
}
public function getName()
{
return 'app_page';
}
}
As you can see i wrote my self in FormType class what choices it has. And now it is always displaying "Active" instead of displaying Entity's value. How should i do that?
Thanks!
Try to change your isActive in the Entity like
protected $isActive ;
and if you want to make a default true you can make it at the construct like :
public function __construct() {
$this->setIsActive = true ;
}
$builder->add('isActive', ChoiceType::class, [
'choices' => [
'Active' => true,
'Inactive' => false
],
'choices_as_values' => true,
'multiple' => true,
'expanded' => true,
'data' => array(true)
]);
A 2021 Solution: setting form choice default value (data) from entity
Sometimes your choice field values exist in another entity table, which means you cannot populate them using default form data_class. You only get the string value that saved in the form entity, resulting to error: ...must be an object or null, string given...
protected EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
// In your form
->add('membershipType', EntityType::class, [
'class' => MembershipType::class,
'label' => 'Enter membership type here',
'data' => $this->em->getRepository(MembershipType::class)->findOneBy([
'name' => $your_value_here
])
])

Two records in database on 1 form

I have an entity Page with fields:
pageID (PK) and tag.
I have an entity PageLocale with fields:
pagelocaleid (PK), description, content, locale, translated and pageID (FK).
My Database Structure:
My entity PageLocale:
<?php
namespace DX\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Pagelocale
*
* #ORM\Table(name="pageLocale", indexes={#ORM\Index(name="fk_pageLocale_page1_idx", columns={"pageID"})})
* #ORM\Entity(repositoryClass="DX\MyBundle\Repository\PagelocaleRepository")
* #ORM\HasLifecycleCallbacks
*/
class Pagelocale
{
/**
* #var string
*
* #ORM\Column(name="description", type="text", length=65535, nullable=true)
*/
private $description;
/**
* #var string
*/
private $descriptionEN;
/**
* #var string
*
* #ORM\Column(name="content", type="text", length=65535, nullable=true)
*/
private $content;
/**
* #var string
*/
private $contentEN;
/**
* #var string
*
* #ORM\Column(name="locale", type="string", length=5, nullable=true)
*/
private $locale;
/**
* #var boolean
*
* #ORM\Column(name="translated", type="boolean", nullable=true)
*/
private $translated;
/**
* #var integer
*
* #ORM\Column(name="pageLocaleID", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $pagelocaleid;
/**
* #var \DX\MyBundle\Entity\Page
*
* #ORM\ManyToOne(targetEntity="DX\MyBundle\Entity\Page")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="pageID", referencedColumnName="pageID")
* })
*/
private $pageid;
/**
* #var string
*/
private $tag;
/**
* Set tag
*
* #param string $tag
* #return Articlelocale
*/
public function setTag($tag)
{
if($this->pageid)
{
$this->pageid->setTag($tag);
}
else
{
$this->tag = $tag;
}
return $this;
}
/**
* Get tag
*
* #return string
*/
public function getTag()
{
if($this->pageid)
{
return $this->pageid->getTag();
}
else
{
return $this->tag;
}
}
/**
* Set description
*
* #param string $description
* #return Pagelocale
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set descriptionEN
*
* #param string $descriptionEN
* #return Pagelocale
*/
public function setDescriptionEN($descriptionEN)
{
$this->descriptionEN = $descriptionEN;
return $this;
}
/**
* Get descriptionEN
*
* #return string
*/
public function getDescriptionEN()
{
return $this->descriptionEN;
}
/**
* Set content
*
* #param string $content
* #return Pagelocale
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set contentEN
*
* #param string $contentEN
* #return Pagelocale
*/
public function setContentEN($contentEN)
{
$this->contentEN = $contentEN;
return $this;
}
/**
* Get contentEN
*
* #return string
*/
public function getContentEN()
{
return $this->contentEN;
}
/**
* Set locale
*
* #param string $locale
* #return Pagelocale
*/
public function setLocale($locale)
{
$this->locale = $locale;
return $this;
}
/**
* Get locale
*
* #return string
*/
public function getLocale()
{
return $this->locale;
}
/**
* Set translated
*
* #param boolean $translated
* #return Pagelocale
*/
public function setTranslated($translated)
{
$this->translated = $translated;
return $this;
}
/**
* Get translated
*
* #return boolean
*/
public function getTranslated()
{
return $this->translated;
}
/**
* Get pagelocaleid
*
* #return integer
*/
public function getPagelocaleid()
{
return $this->pagelocaleid;
}
/**
* Set pageid
*
* #param \DX\MyBundle\Entity\Page $pageid
* #return Pagelocale
*/
public function setPageid(\DX\MyBundle\Entity\Page $pageid = null)
{
$this->pageid = $pageid;
return $this;
}
}
I'm using the Sonata Admin Bundle but that doesn't really matter in this case.
As you can see in my entity I've manually added contentEN and descriptionEN because I want the content & description in 2 languages.
And I want to be able to do this in one form and not in 2 steps (create for each language content).
Now I have in my form:
$formMapper
->add('tag', 'text', array('label' => 'Tag'))
->add('description', 'text', array('label' => 'Beschrijving'))
->add('descriptionEN', 'text', array('label' => 'Beschrijving Engels'))
->add('content', 'textarea', array('label' => 'Tekst', 'attr' => array('class' => 'ckeditor'), 'help' =>
'My help text.'))
->add('contentEN', 'textarea', array('label' => 'Tekst Engels', 'attr' => array('class' => 'ckeditor'), 'help' =>
'My help text.'))
;
I can create 2 different entities and set the locale, that's no problem. But when I want to edit them the contentEN & descriptionEN are logically not filled in. How can I make sure I get the description and content where locale = .. ?
I know the contentEN & descriptionEN are not the way to do this. But I just wanted to clarify the problem.
If you want to retrieve some specific content based on the locale, you need to create an array where you set your fields ordered by locale and create a query that uses this array.
You could do it this way :
class PageLocaleManager
{
public function getParametersLocale()
{
// retrieve the current locale.
$locale = $this->get('request')->getLocale();
// set the fields you want retrieve based on the locale, you have to set an alias before the name for the query.
$parameters = array(
'fr' => array('pl.description', 'pl.content'),
'en' => array('pl.descriptionEN', 'pl.contentEN')
);
// if the locale is set return the parameters related to it, else return the default parameters (in this case default = fr)
return (isset($parameters[$locale]) ? $parameters[$locale] : $parameters['fr'];
}
}
class PageLocaleRepository
{
public function findLocalePageById($parameters, $pageId)
{
return $this->createQueryBuilder('pl')
->select($parameters)
->where('pl.page = :pageId')
->setParameter('pageId', $id)
->getQuery()
->getResult();
}
}
Then when you want to retrieve the page content for a specific locale you have to do :
$pageId = 1;
$parameters = $this->get('page_locale.manager')->getParametersLocale();
$pageLocaleEntity = $this->getDoctrine()->getRepository('TestBundle:PageLocale')->findLocalePageById($parameters, $pageId);
Also you shouldn't use this schema in your project in my opinion, if you want to add more languages, you will have to duplicate fields.
Update
You have to use events prePersist / preUpdate if you want to create / update 2 entity from the data of the same form.
You can add in your Sonata Admin a prePersist method (http://sonata-project.org/bundles/admin/master/doc/reference/saving_hooks.html) and create a new Entity based on the data of your form.
class PageLocaleAdmin extends Admin
{
public function prePersist($object)
{
$pageLocaleEnEntity = new PageLocale();
$pageLocaleEnEntity->setDescriptionEN($object->getDescriptionEN());
$pageLocaleEnEntity->setContentEN($object->getContentEN());
$this->getConfigurationPool()->getContainer()->get('doctrine')->persist($pageLocaleEnEntity);
}
}
Update 2
Ok, i didn't see that the field descriptionEN and contentEN are not in your model.
To fill the descriptionEN and contentEN on Edit you have to retrieve the related PageLocale and set the locale value, you could do :
class PageLocaleAdmin extends Admin
{
public function configureFormFields(FormMapper $formMapper)
{
$locale = 'en';
if ($this->subject->getId() !== null) {
$pageLocaleEntity = $this->getConfigurationPool()->getContainer()->get('doctrine')->getRepository('DXMyBundle:PageLocale')->findOneby($this->subject->getId(), $locale);
$descriptionEN = $pageLocaleEntity->getDescriptionEN();
$contentEN = $pageLocaleEntity->getContentEN();
} else {
$pageLocaleEntity = null;
$descriptionEN = null;
$contentEN = null;
}
$formMapper
->add('description', 'text', array('label' => 'Beschrijving'))
->add('descriptionEN', 'text', array('label' => 'Beschrijving Engels', 'data' => $descriptionEN))
->add('content', 'textarea', array('label' => 'Tekst', 'attr' => array('class' => 'ckeditor'), 'help' => 'My help text.'))
->add('contentEN', 'textarea', array('label' => 'Tekst Engels', 'attr' => array('class' => 'ckeditor'), 'help' => 'My help text.', 'data' => $contentEN));
}
}

symfony2 Catchable Fatal Error: Object of class could not be converted to string

I have this Entity defined:
<?php
namespace Apw\BlackbullBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Categories
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Apw\BlackbullBundle\Entity\CategoriesRepository")
*/
class Categories
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="categories_image", type="string", length=64, nullable = true)
*/
private $categoriesImage;
/**
* #var integer
*
* #ORM\Column(name="parent_id", type="integer", nullable = true, options={"default":0})
*/
private $parentId;
/**
* #var integer
*
* #ORM\Column(name="sort_order", type="integer", nullable = true, options={"default":0})
*/
private $sortOrder;
/**
* #var \DateTime
*
* #ORM\Column(name="date_added", type="datetime", nullable = true)
*/
private $dateAdded;
/**
* #var \DateTime
*
* #ORM\Column(name="last_modified", type="datetime", nullable = true)
*/
private $lastModified;
/**
* #var boolean
*
* #ORM\Column(name="categories_status", type="boolean", nullable = true, options={"default" = 1})
*/
private $categoriesStatus;
/**
* #ORM\OneToMany(targetEntity="CategoriesDescription", mappedBy="category", cascade={"persist", "remove"})
*/
private $categoryDescription;
/**
* #ORM\ManyToMany(targetEntity="Products", mappedBy="categories")
**/
private $products;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set categoriesImage
*
* #param string $categoriesImage
* #return Categories
*/
public function setCategoriesImage($categoriesImage)
{
$this->categoriesImage = $categoriesImage;
return $this;
}
/**
* Get categoriesImage
*
* #return string
*/
public function getCategoriesImage()
{
return $this->categoriesImage;
}
/**
* Set parentId
*
* #param integer $parentId
* #return Categories
*/
public function setParentId($parentId)
{
$this->parentId = $parentId;
return $this;
}
/**
* Get parentId
*
* #return integer
*/
public function getParentId()
{
return $this->parentId;
}
/**
* Set sortOrder
*
* #param string $sortOrder
* #return Categories
*/
public function setSortOrder($sortOrder)
{
$this->sortOrder = $sortOrder;
return $this;
}
/**
* Get sortOrder
*
* #return string
*/
public function getSortOrder()
{
return $this->sortOrder;
}
/**
* Set dateAdded
*
* #param \DateTime $dateAdded
* #return Categories
*/
public function setDateAdded($dateAdded)
{
$this->dateAdded = $dateAdded;
return $this;
}
/**
* Get dateAdded
*
* #return \DateTime
*/
public function getDateAdded()
{
return $this->dateAdded;
}
/**
* Set lastModified
*
* #param \DateTime $lastModified
* #return Categories
*/
public function setLastModified($lastModified)
{
$this->lastModified = $lastModified;
return $this;
}
/**
* Get lastModified
*
* #return \DateTime
*/
public function getLastModified()
{
return $this->lastModified;
}
/**
* Constructor
*/
public function __construct()
{
$this->categoryDescription = new ArrayCollection();
$this->products = new ArrayCollection();
}
/**
* Add categoryDescription
*
* #param \Apw\BlackbullBundle\Entity\CategoriesDescription $categoryDescription
* #return Categories
*/
public function addCategoryDescription(\Apw\BlackbullBundle\Entity\CategoriesDescription $categoryDescription)
{
$this->categoryDescription[] = $categoryDescription;
return $this;
}
/**
* Remove categoryDescription
*
* #param \Apw\BlackbullBundle\Entity\CategoriesDescription $categoryDescription
*/
public function removeCategoryDescription(\Apw\BlackbullBundle\Entity\CategoriesDescription $categoryDescription)
{
$this->categoryDescription->removeElement($categoryDescription);
}
/**
* Get categoryDescription
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategoryDescription()
{
return $this->categoryDescription;
}
/**
* Add products
*
* #param \Apw\BlackbullBundle\Entity\Products $products
* #return Categories
*/
public function addProduct(\Apw\BlackbullBundle\Entity\Products $products)
{
$this->products[] = $products;
return $this;
}
/**
* Remove products
*
* #param \Apw\BlackbullBundle\Entity\Products $products
*/
public function removeProduct(\Apw\BlackbullBundle\Entity\Products $products)
{
$this->products->removeElement($products);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
/**
* Set categoriesStatus
*
* #param boolean $categoriesStatus
* #return Categories
*/
public function setCategoriesStatus($categoriesStatus)
{
$this->categoriesStatus = $categoriesStatus;
return $this;
}
/**
* Get categoriesStatus
*
* #return boolean
*/
public function getCategoriesStatus()
{
return $this->categoriesStatus;
}
}
Then I have this method in my controller for handle form submission:
<?php
namespace Apw\BlackbullBundle\Controller;
use Apw\BlackbullBundle\Entity\Categories;
use Apw\BlackbullBundle\Entity\CategoriesDescription;
use Apw\BlackbullBundle\Form\CategoriesType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
class CategoriesController extends Controller
{
/**
* #Security("has_role('ROLE_ADMIN')")
* #Route("/createCategory")
* #Template()
*/
public function createCategoryAction(Request $request){
$category = new Categories();
$categoryDesc = new CategoriesDescription();
$category->addCategoryDescription($categoryDesc);
$categoryDesc->setCategory($category);
$form = $this->createForm(new CategoriesType(), $category);
$form->handleRequest($request);
if($form->isValid()){
//exit(\Doctrine\Common\Util\Debug::dump($category));
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->persist($categoryDesc);
$em->flush();
return $this->redirect($this->generateUrl('apw_blackbull_categories_showcategories'));
}
return array(
'form' => $form->createView()
);
}
}
And finally this is my CategoryType.php:
<?php
namespace Apw\BlackbullBundle\Form;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CategoriesType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('categoryDescription', 'collection',
array(
'type' => new CategoriesDescriptionType(),
'allow_add' => true,
'options' => array('data_class' => 'Apw\BlackbullBundle\Entity\CategoriesDescription'),
'by_reference' => false,
))
->add('categoriesImage', null, array('label'=>'Foto:'))
->add('categoriesStatus', null, array('label'=>'Stato:'))
->add('parentId', 'entity', array( //provare a mettere una querybuiler
'class' => 'ApwBlackbullBundle:CategoriesDescription',
'property' => 'categoriesName',
'empty_value' => 'Scegliere una categoria',
'required' => false,
'label' => 'Crea in:'))
->add('salva','submit')
->add('azzera','reset')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Apw\BlackbullBundle\Entity\Categories',
));
}
/**
* #return string
*/
public function getName()
{
return 'categories';
}
}
When I try to save data I get this error:
An exception occurred while executing 'INSERT INTO Categories
(categories_image, parent_id, sort_order, date_added, last_modified,
categories_status) VALUES (?, ?, ?, ?, ?, ?)' with params ["as", {},
null, null, null, 1]:
Catchable Fatal Error: Object of class
Apw\BlackbullBundle\Entity\CategoriesDescription could not be
converted to string
What I'm doing wrong?
You need to implement the __toString() method in your Apw\BlackbullBundle\Entity\CategoriesDescription.
You could do:
public function __toString() {
return $this->name;
}
For Symfony 3.x
According with Symfony docs v3.x you should use choice_label property to specify the entity field name to be used here.
->add('categoryDescription', 'collection',
array(
'type' => new CategoriesDescriptionType(),
'allow_add' => true,
'options' => array('data_class' => 'Apw\BlackbullBundle\Entity\CategoriesDescription'),
'choice_label' => 'name',
'by_reference' => false,
))
I got the same error but i tweaked it a little bit by adding:
public function __toString()
{
return (string) $this->name;
}
I'm sure i was getting null instead of a string value. (I was working with sonata-project).
so I solved the problem by get the value of relative parent in the method $form->isValid()
public function createCategoryAction(Request $request){
$category = new Categories();
$categoryDesc = new CategoriesDescription();
$category->addCategoryDescription($categoryDesc);
$categoryDesc->setCategory($category);
$form = $this->createForm(new CategoriesType(), $category);
$form->handleRequest($request);
if($form->isValid()){
//exit(\Doctrine\Common\Util\Debug::dump($parentCategory->getId()));
$em = $this->getDoctrine()->getManager();
if(!$category->getParentId()){
$category->setParentId(0);
}else{
// get parent id value from input choice
$parent = $category->getParentId();
$parentCategory = $parent->getCategory();
// end
$category->setParentId($parentCategory->getId());
}
$em->persist($category);
$em->persist($categoryDesc);
$em->flush();
return $this->redirect($this->generateUrl('apw_blackbull_categories_showcategories'));
}
return array(
'form' => $form->createView()
);
}
thanks!
You can also use the property accessor into your form:
->add('categoryDescription', 'collection',
array(
'type' => new CategoriesDescriptionType(),
'allow_add' => true,
'options' => array('data_class' => 'Apw\BlackbullBundle\Entity\CategoriesDescription'),
'by_reference' => false,
))
And add 'property' => 'name' in your CategoriesDescriptionType.
By the way the #CoachNono answer is ok too.

Symfony2 form collection field not showing up

I'll explain what I'm trying to do.
I have the following entities, Dealers Brands and Types.
1 Dealer can have many brands associated and each relation will have one type associated as well, that's why is a manyToMany relationship with an intermediate table.
I want to create a Form to Add dealers to our system and when doing so they can choose which brands are associated (there are few brands so we want to display them as checkboxes) and of what type, but trying to show this form with the brands is been tricky until now.
Brand Entity
/**
* Brand
*
* #ORM\Table()
* #ORM\Entity
*/
class Brand
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="TypeBrand")
* #ORM\JoinTable(name="brand_type",
* joinColumns={#ORM\JoinColumn(name="brand_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="type_id", referencedColumnName="id")}
* )
*/
private $type;
/**
* #ORM\OneToMany(targetEntity="DealerBrand", mappedBy="brand")
*/
private $dealerBrand;
public function __construct()
{
$this->type = new ArrayCollection();
$this->dealerBrand = new ArrayCollection();
}
public function __toString()
{
return $this->getName();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Brand
*/
public function setName($name)
{
$this->name = $name;
$this->setSlug($name);
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
public function addType(TypeBrand $type)
{
$this->type[] = $type;
}
public function getType()
{
return $this->type;
}
public function addDealerBrand($dealerBrand)
{
$this->dealerBrand[] = $dealerBrand;
}
public function getDealerBrand()
{
return $this->dealerBrand;
}
}
Dealer Entity
/**
* Dealer
*
* #ORM\Table("dealer")
* #ORM\Entity(repositoryClass="Project\DealersBundle\Entity\Repository\DealerRepository")
* #Gedmo\Loggable
*/
class Dealer
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
* #Assert\NotBlank()
* #Assert\Length(min = "10")
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="DealerBrand", mappedBy="dealer")
*
*/
private $dealerBrand;
public function __construct()
{
$this->dealerBrand = new ArrayCollection();
}
public function __toString()
{
return $this->getName();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Dealer
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
public function addDealerBrand($dealerBrand)
{
$this->dealerBrand[] = $dealerBrand;
}
public function getDealerBrand()
{
return $this->dealerBrand;
}
}
DealerBrand Entity
/**
* DealerBrand
*
* #ORM\Table("dealer_brand")
* #ORM\Entity
*/
class DealerBrand
{
/**
* #var integer
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Dealer", inversedBy="dealerBrand")
*/
private $dealer;
/**
* #var integer
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Brand", inversedBy="dealerBrand")
*/
private $brand;
/**
* Set dealer
*
* #param integer $dealer
* #return DealerBrand
*/
public function setDealer($dealer)
{
$this->dealer = $dealer;
return $this;
}
/**
* Get dealer
*
* #return integer
*/
public function getDealer()
{
return $this->dealer;
}
/**
* Set brand
*
* #param integer $brand
* #return DealerBrand
*/
public function setBrand($brand)
{
$this->brand = $brand;
return $this;
}
/**
* Get brand
*
* #return integer
*/
public function getBrand()
{
return $this->brand;
}
}
Now this are my formtypes
DealerBrandType
class DealerBrandType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('brand', 'entity', array(
'class' => 'Project\DealersBundle\Entity\Brand',
'property' => 'name',
'multiple' => true,
'expanded' => true,
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Project\DealersBundle\Entity\DealerBrand'
)
);
}
public function getName()
{
return 'DealerBrand';
}
}
DealerType
class DealerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('dealerBrand', 'collection', array(
'type' => new DealerBrandType(),
'allow_add' => true,
'allow_delete' => true,
))
->add('Save', 'submit');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Project\DealersBundle\Entity\Dealer'
)
);
}
public function getName()
{
return 'dealerType';
}
}
And this is my controller
public function addAction()
{
$dealer = new Dealer();
$form = $this->createForm(new DealerType(), $dealer);
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$this->getDoctrine()->getManager()->persist($dealer);
$this->getDoctrine()->getManager()->flush();
return $this->redirect($this->generateUrl('dealers_list'));
}
return $this->render(
'ProjectDealersBundle:Dealers:AddDealer.html.twig',
array(
'form' => $form->createView()
)
);
}
If this is not the correct approach please tell me, tell me also if you see bad code, that way I can improve
* EDIT *
This is the result i need
http://tinypic.com/r/24pkzdc/8
You can see there the brands and so .. so the idea is that when saving the Dealer you also save the association with the brands
* END EDIT *
Thank you!
I'm not sure this is the best answer ever but you can achieve it by making a custom query in your form :
->add('contact', 'entity', array(
'class' => 'AcmeBundle:Entity'
'query_builder' => function ( \acme\bundle\Entity\entityRepository $c){
$qb = $c->createQueryBuilder('a');
return $qb->orderBy('a.nom', 'ASC')
->join('a.categories', 'c')
->where( $qb->expr()->in ( 'c.id', ':listCategories') )
->setParameter( 'listCategories', array (
7,
));
},
'attr' => array( 'class' => 'other')
))
;
}
by using an 'entity' field type, i can inject custom query with the option 'query_builder' ( for more information : http://symfony.com/doc/current/reference/forms/types/entity.html )
then, inside i declare an anonymous function ( http://php.net/manual/en/functions.anonymous.php ) to create my custom query with the createQueryBuilder.
With the createQueryBuilder , you create your custom query ( here i had almost like you a many to many relationship and i wanted to get only some of them with a filtering array ( the set parameter ) .
here is the doc for custom query : http://symfony.com/doc/current/book/doctrine.html
the results of the query, if not null , will be displayed in your form
/** ALTERNATIVE ANSWER **/
if you want to display your dealers AND brands with a tree in your select then your have to :
1) make a query to return an object container your dealers and brands
2) make an array with depth that the select will display as a tree :
here is an example to illustrate :
->add('contact', 'entity', array(
'class' => 'AcmeBundle:Entity'
'query_builder' => function ( \acme\bundle\Entity\entityRepository $c){
$dealers = $c->yourFunctionThatReturnesDealers('d');
$dealersGroupedByBrands = array();
foreach ( $dealers as $dealer) {
foreach ($dealers->getBrands() as $brand) {
$dealersGroupedByBrands[$brand->getName()][] = $dealer;
}
return $dealersGroupedByBrands;
},
'attr' => array( 'class' => 'other')
))
;
}
pretty cool , no ?

Categories