I have a list of Doctrine entities (called "Circuit") and would like to generate a form listing them within a <table> and add a way to tick them for mass deletion (kind of what Sonata Admin does, without the need for an admin class).
I've looked everywhere but I can't figure out for the life of me what to do. There is just one layer to this class (plain old object), and every time I try to add a collection type to my form builder I get the following error:
Neither the property "circuits" nor one of the methods "getCircuits()", "circuits()", "isCircuits()", "hasCircuits()", "__get()", "__call()" exist and have public access in class "NetDev\CoreBundle\Entity\Circuit".
Am I supposed to create a "proxy" class to create a collection of circuits ? Did I miss something ?
All the howtos I found so far are using a "master" class like "Article" and a collection of child classes like "Categories" which doesn't apply to my present issue.
Here is my CircuitsController.php (I use the "addAction" for the tests, eventually everything will be located in indexAction):
<?php
namespace NetDev\WebManagerBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use NetDev\CoreBundle\Form\CircuitType;
use NetDev\CoreBundle\Entity\Circuit;
class CircuitsController extends Controller {
public function indexAction($page = 1) {
$listCircuits = $this->getDoctrine()->getManager()->getRepository('NetDevCoreBundle:Circuit')->findAll();
$content = $this->get('templating')->render('NetDevWebManagerBundle:Circuits:index.html.twig',
array('listCircuits' => $listCircuits));
return new Response($content);
}
public function addAction(Request $request) {
$circuit = new Circuit();
$form = $this->createForm(new CircuitType(), $circuit);
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
/* some action that is not actually relevant */
}
return new Response($this->get('templating')->render('NetDevWebManagerBundle:Circuits:add.html.twig',
array('circuit' => $circuit,
'form' => $form->createView())));
}
The CircuitType.php file:
<?php
namespace NetDev\CoreBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CircuitType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('circuits', 'collection', array('type' => 'entity', 'allow_add' => true,
'allow_delete' => true, 'by_reference' => false,
'label' => false,
'options' => array('class' => 'NetDevCoreBundle:Circuit',
'label' => false, 'multiple' => true,
'expanded' => true)
))
/* ->add('vlanId', 'integer', array('required' => true, 'label' => 'VLAN ID')) */
/* ->add('popOut', 'text', array('required' => true, 'label' => 'Injecting PoP', */
/* 'max_length' => 3)) */
/* ->add('popsIn', 'textarea', array('required' => true, 'label' => 'Listening PoP')) */
/* ->add('bandwidth', 'integer', array('label' => 'Bandwidth')) */
/* ->add('xconnectId', 'text', array('label' => 'Cross-connect ID')) */
/* ->add('Create', 'submit') */
;
}
/**
* #return string
*/
public function getName()
{
return 'netdev_corebundle_circuit';
}
}
And finally, the Circuit.php entity file:
<?php
namespace NetDev\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Circuit
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="NetDev\CoreBundle\Entity\CircuitRepository")
*/
class Circuit
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="vlan_id", type="integer")
* #Assert\Type(type="int")
* #Assert\Range(min="1", max="4096")
*/
private $vlanId;
/**
* #var array
*
* #ORM\Column(name="pop_out", type="array")
* #Assert\NotBlank()
* #Assert\Length(max=3)
*/
private $popOut;
/**
* #var array
*
* #ORM\Column(name="pops_in", type="array")
* #Assert\NotBlank()
*/
private $popsIn;
/**
* #var integer
*
* #ORM\Column(name="bandwidth", type="integer")
* #Assert\Type(type="int")
*/
private $bandwidth;
/**
* #var string
*
* #ORM\Column(name="xconnect_id", type="string", length=255)
* #Assert\NotBlank()
* #Assert\Length(max="255")
*/
private $xconnectId;
/* Getters and Setters stripped for clarity's sake */
public function __toString() {
return "{$this->vlanId}-{$this->popOut}";
}
}
If you need the twig template tell me, I haven't added it because I am not even close to having something outputted aside from that Exception.
As said by #Cerad, the answer here was to pass the result of
$listCircuits = $this->getDoctrine()->getManager()->getRepository('NetDevCoreBundle:Circuit')->findAll();
directly to the collection. Everything worked nicely afterwards.
Related
I'm working with Symfony 4 and Sonata Admin.
I'm trying to use a CollectionType in my FormBuilder for create an order in my admin dashboard and i get this error:
Warning: spl_object_hash() expects parameter 1 to be object, array given
I know what it mean, but I do not find how to fix it :(
OrderAdmin
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Sonata\AdminBundle\Form\Type\ModelListType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use App\Entity\User;
use App\Entity\OrderProduct;
use App\Form\Type\OrderProductFormType;
class OrderAdmin extends AbstractAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper->add('state', ChoiceType::class, array(
'choices' => array(
'PROCESSING' => 'PROCESSING',
'DONE' => 'DONE'
)
));
$formMapper->add('user', EntityType::class, [
'class' => User::class
]);
$formMapper->add('orderProducts', CollectionType::class, array(
'entry_type' => OrderProductFormType::class,
'allow_add' => true,
'allow_delete' => true
));
}
?>
OrderProductFormType
<?php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use App\Entity\Product;
use App\Entity\Size;
use App\Entity\Material;
use App\Entity\Shape;
class OrderProductFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('product', EntityType::class, [
'class' => Product::class
]);
$builder->add('quantity', IntegerType::class);
$builder->add('size',EntityType::class, [
'class' => Size::class,
'required' => false
]);
$builder->add('shape', EntityType::class, [
'class' => Shape::class,
'required' => false
]);
$builder->add('material', EntityType::class, [
'class' => Material::class,
'required' => false
]);
}
public function getBlockPrefix()
{
return 'app_order_product';
}
}
Order
<?php
namespace App\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use App\Entity\OrderProducts;
use App\Entity\Address;
use App\Entity\User;
/**
* #ORM\Entity
* #ORM\Table(name="`order`")
*/
class Order
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $uniqId;
/**
* #ORM\Column(type="string")
*/
protected $state;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="orders")
* #ORM\JoinColumn(nullable=true)
*/
protected $user;
/**
* #ORM\OneToMany(targetEntity="OrderProduct", mappedBy="order", orphanRemoval=true, cascade="persist")
* #ORM\JoinColumn(nullable=true)
*/
protected $orderProducts;
/**
* #ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
public function __construct()
{
$this->state = 'PROCESSING';
$this->orderProducts = new ArrayCollection();
$this->createdAt = new \Datetime('now');
$this->uniqId = uniqid();
}
OrderProduct
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use App\Entity\User;
use App\Entity\Product;
use App\Entity\Size;
use App\Entity\Shape;
use App\Entity\Material;
use App\Entity\Order;
/**
* #ORM\Entity
* #ORM\Table(name="order_product")
*/
class OrderProduct {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Product", cascade={"persist"})
* #ORM\JoinColumn(nullable=true, onDelete="CASCADE")
*/
private $product;
/**
* #ORM\ManyToOne(targetEntity="Size")
* #ORM\JoinColumn(nullable=true)
*/
private $size;
/**
* #ORM\ManyToOne(targetEntity="Material")
* #ORM\JoinColumn(nullable=true)
*/
private $material;
/**
* #ORM\ManyToOne(targetEntity="Shape")
* #ORM\JoinColumn(nullable=true)
*/
private $shape;
/**
* #ORM\Column(name="created_at", type="datetime")
*/
private $createdAt;
/**
* #ORM\Column(name="quantity", type="integer")
*/
private $quantity;
/**
* #ORM\ManyToOne(targetEntity="Order", inversedBy="orderProducts")
* #ORM\JoinColumn(nullable=true)
*/
private $order;
public function __construct() {
$this->createdAt = new \Datetime('now');
}
stackTrace
ErrorException:
Warning: spl_object_hash() expects parameter 1 to be object, array given
at vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1741
at Doctrine\ORM\UnitOfWork->doPersist(array('product' => object(Product)), array('000000001e398cf400000000645d817d' => object(Order)))
(vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:2323)
at Doctrine\ORM\UnitOfWork->cascadePersist(object(Order), array('000000001e398cf400000000645d817d' => object(Order)))
(vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1785)
at Doctrine\ORM\UnitOfWork->doPersist(object(Order), array('000000001e398cf400000000645d817d' => object(Order)))
(vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1722)
at Doctrine\ORM\UnitOfWork->persist(object(Order))
(vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:591)
at Doctrine\ORM\EntityManager->persist(object(Order))
(vendor/sonata-project/doctrine-orm-admin-bundle/src/Model/ModelManager.php:173)
at Sonata\DoctrineORMAdminBundle\Model\ModelManager->update(object(Order))
(vendor/sonata-project/admin-bundle/src/Admin/AbstractAdmin.php:663)
at Sonata\AdminBundle\Admin\AbstractAdmin->update(object(Order))
(vendor/sonata-project/admin-bundle/src/Controller/CRUDController.php:355)
at Sonata\AdminBundle\Controller\CRUDController->editAction('14')
(vendor/symfony/http-kernel/HttpKernel.php:149)
at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
(vendor/symfony/http-kernel/HttpKernel.php:66)
at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
(vendor/symfony/http-kernel/Kernel.php:188)
at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
(public/index.php:37)
I understand he receive an array when he expects an object, but why i got an array and not an object OrderProduct with the CollectionType Field?! Maybe i have something to do for override this result ?
Thank you to take a look on my post !
Summary
I have three entities:
User, Organisation and OrganistionUser.
I created an Organisation form with an embedded collection of OrganisationUsers. This is working. What i can't get to work is that only Users without an assocciation (to an organisation) are 'queried' and show up in the selectbox in the OrganisationForm.
Details
Entitie 1: Organisation
class Organisation
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*
* #ORM\Column(type="string", nullable=true)
*/
protected $name;
/**
* #ORM\OneToMany(targetEntity="OrganisationUser", mappedBy="organisation_id", cascade={"persist", "remove"}, orphanRemoval=true)
*
* #Expose
*/
private $users;
Entitie 2: User
(I extended the FOSUserBundle)
<?php
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*/
protected $username;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
* #ORM\OneToMany(targetEntity="OrganisationUser", mappedBy="user_id", cascade={"ALL"}, orphanRemoval=true)
*/
protected $organisationusers;
I have succesfully embedded a collection of forms. In my 'create-new-organisation-form' i can add many users, and they are persisted to the database.
They are persisted in the OrganisationUser table (because i only want to associate EXISTING users to an organisation).
The OrganisationUser entity (so actually i have three entities) looks as:
Entitie 3: OrganisationUser
<?php
class OrganisationUser
{
protected $id;
/**
* #var ProjectId
* #ORM\ManyToOne(targetEntity="Organisation", inversedBy="organisationusers")
* #ORM\JoinColumn(name="organisation_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $organisation_id;
/**
* #var ProjectId
* #ORM\ManyToOne(targetEntity="User", inversedBy="organisationusers")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $user_id;
NOW, in my form (OrganisionType) i embedded a collection of OrganisationUser. But i want to manipulate the data (i do not want to show all users. Only the user that are NOT associated to an organisation). How can i achieve this. I already looked here and here but it's no solution for this question so i created a new question.
The OrganistionType:
<?php
class OrganisationType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('address')
->add('postal')
->add('city')
->add('phone')
->add('email')
->add('role');
$builder->add('users', 'collection', array(
'entry_type' => new OrganisationUserType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false,
));
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Organisation',
));
}
}
And the OrganisationUserType:
<?php
class OrganisationUserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user_id', null ,array(
'label' => 'User'
))
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\OrganisationUser'
));
}
}
The controller
<?php
/**
* Organisation controller.
*
* #Route("/organisation")
*/
class OrganisationController extends Controller
{
/**
* Creates a new Organisation entity.
*
* #Route("/new", name="organisation_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$organisation = new Organisation();
$form = $this->createForm('AppBundle\Form\OrganisationType', $organisation);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($organisation);
$em->flush();
return $this->redirectToRoute('organisation_show', array('id' => $organisation->getId()));
}
return $this->render('organisation/new.html.twig', array(
'organisation' => $organisation,
'form' => $form->createView(),
));
}
This is how it looks like when its rendered:
And, let's say if testuser3 is already associated with an organisation i dont want him to show up in the dropdown :)
Ok, i found it. In my OrganisationType i use the OrganisationUserType to embed the collection of users. So in the OrganisationUserType i had to query for the result:
Off course this is not the right query but i know now where to manipulate the data. Now i can go and search for the right query :)
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Doctrine\ORM\EntityRepository;
class OrganisationUserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user_id', 'entity', array(
'class' => 'AppBundle:User',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.id > :id')
->setParameter('id', '1')
->orderBy('u.username', 'ASC');
},
'choice_label' => 'username',
'required' => false,
));
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\OrganisationUser',
));
}
}
I have 3 form types (SearchForm - SearchField - SearchFieldType), each one including next like this:
SearchFormType:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class SearchFormType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fields', 'collection', array('type' => new SearchFieldType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false))
->add('submit', 'submit', array('label' => "Buscar"))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\SearchForm',
'allow_extra_fields' => true,
'csrf_protection' => false,
'validation_groups' => false,
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_searchform';
}
}
SearchFieldType:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class SearchFieldType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'hidden')
->add('slug', 'hidden')
->add('value')
->add('choices')
->add('type', new SearchFieldTypeType())
->add('actionFilter')
->add('actionHighlight')
->add('actionShow')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\SearchField'
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_searchfield';
}
}
SearchFieldTypeType:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use AppBundle\Entity\SearchOperator;
class SearchFieldTypeType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$entity = $builder->getData();
$builder
->add('name', 'hidden')
->add('operators', 'entity', array('class' => 'AppBundle:SearchOperator',
'multiple' => false,
'expanded' => false))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\SearchFieldType'
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_searchfieldtype';
}
}
The form renders properly, but when I submit and try to do $form->handleRequest($request) I get an exception:
Neither the property "operators" nor one of the methods "addOperator()"/"removeOperator()", "setOperators()", "operators()", "__set()" or "__call()" exist and have public access in class "AppBundle\Entity\SearchFieldType"
That's not true actually, as those methods exist and work correctly:
AppBundle\Entity\SearchFieldType:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\SearchOperator;
/**
* SearchField
*
* #ORM\Table()
* #ORM\Entity
*/
class SearchFieldType
{
/**
* #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=100, nullable=true)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="SearchOperator", cascade={"persist", "remove"})
* #ORM\JoinTable(
* joinColumns={#ORM\JoinColumn(name="type_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="operator_id", referencedColumnName="id")}
* )
**/
private $operators;
/**
* Constructor
*/
public function __construct()
{
$this->operators = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return SearchFieldType
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Add operator
*
* #param SearchOperator $operator
*
* #return SearchFieldType
*/
public function addOperator(SearchOperator $operator)
{
$this->operators[] = $operator;
return $this;
}
/**
* Remove operator
*
* #param SearchOperator $operator
*/
public function removeOperator(SearchOperator $operator)
{
$this->operators->removeElement($operator);
}
/**
* Get operator
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getOperators()
{
return $this->operators;
}
public function __toString()
{
return $this->name;
}
}
Stack trace:
in vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php at line 460 +
at PropertyAccessor ->writeProperty (object(SearchFieldType), 'operators', object(SearchOperator))
in vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php at line 104 +
at PropertyAccessor ->setValue (object(SearchFieldType), object(PropertyPath), object(SearchOperator))
in vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php at line 93 +
at PropertyPathMapper ->mapFormsToData (object(RecursiveIteratorIterator), object(SearchFieldType))
in vendor/symfony/symfony/src/Symfony/Component/Form/Form.php at line 633 +
at Form ->submit (array('operators' => '156', 'name' => 'string'), true)
in vendor/symfony/symfony/src/Symfony/Component/Form/Form.php at line 577 +
at Form ->submit (array('type' => array('operators' => '156', 'name' => 'string'), 'value' => 'felipe', 'name' => 'Nombre', 'slug' => 'nombre'), true)
in vendor/symfony/symfony/src/Symfony/Component/Form/Form.php at line 577
EDIT:
Controller Code :
$searchFormEntity = new SearchForm();
$searchFormWithValues = $this->createForm(new SearchFormType(), $searchFormEntity, array(
'action' => $this->generateUrl('candidato'),
'method' => 'POST'
));
$searchFormWithValues->add('submit', 'submit', array('label' => 'Buscar'));
$searchFormWithValues->handleRequest($request);
Well you have a ManyToMany relation, so it would make sense to have the operators field be a collection. However you defined it as an entity, so now the form expects to have the setOperators and getOperators methods as entity implies a ManyToOne or OneToOne relationship.
I think you need to change the statement in the class SearchFieldTypeType where adding the operators attribute to be the same as what you did before for fields in SearchFormType if you want to keep the ManyToMany relationship.
I want to use the query_builder in a Symfony2 FormType, I tried to make a simple request in my Postgresql shema using Doctrine DQL. It return me an error and i don't really understand how fix it :
PDOException: SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near "" AS id0 FROM ""
LINE 1: SELECT "0_."id" AS id0 FROM "DATA_WAREHOUSE"."WindFarm" "0_
This is the Windfarm class :
<?php
namespace Base\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* WindFarm
*
* #ORM\Table(name="""DATA_WAREHOUSE"".""WindFarm""")
* #ORM\Entity(repositoryClass="Base\CoreBundle\Repository\WindFarmRepository")
*/
class WindFarm
{
/**
* #ORM\OneToMany(targetEntity="Base\CoreBundle\Entity\Turbine", mappedBy="windFarm")
*/
private $turbines;
/**
* #var string $id
*
* #ORM\Column(name="""id""", type="string", length=32)
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\SequenceGenerator(sequenceName="""DATA_WAREHOUSE"".""WindFarm_id_seq""", allocationSize=1, initialValue=1)
*/
private $id;
/**
* #var string $name
*
* #ORM\Column(name="""name""", type="text", nullable=true)
*/
private $name;
/**
* #var string $alias
*
* #ORM\Column(name="""alias""", type="string", length=32, nullable=true)
*/
private $alias;
/**
* #var string $location
*
* #ORM\Column(name="""location""", type="text", nullable=true)
*/
private $location;
/**
* #var string $sncNbr
*
* #ORM\Column(name="""sncNbr""", type="string", length=8, nullable=true)
*/
private $sncNbr;
}
The DQL request in the WindFarmRepository:
public function getWindFarmsAndTurbines(){
$qb = $this->createQueryBuilder()
->select('wft')
->from('BaseCoreBundle:WindFarm', 'wft')
->leftJoin('wft.turbines', 't')
->addSelect('t')
->orderBy('t.alias');
return $qb;
}
The FormType :
<?php
namespace Base\CoreBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Base\CoreBundle\Repository\WindFarmRepository;
class TurbineStatusCodeType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('exportBegin', 'date', array('input' => 'datetime',
'widget' => 'single_text',
'format' => 'yyyy-MM-dd',))
->add('exportEnd', 'date', array('input' => 'datetime',
'widget' => 'single_text',
'format' => 'yyyy-MM-dd',))
->add('arrayId', 'textarea')
->add('turbines', 'entity', array('class' => 'BaseCoreBundle:WindFarm',
'property' => 'name',
'multiple' => true,
'expanded' => true,
'query_builder' => function(WindFarmRepository $repo){
return $repo->getWindFarmsAndTurbines();
}))
->add('save', 'submit');
}
/**
* #return string
*/
public function getName()
{
return 'base_corebundle_turbineStatusCodeType';
}
}
apparently, the problem is situated in the quote, I tried many solutions, but nothing work.
All of your Doctrine annotations are incorrect and have extra double-quotes.
#ORM\Column(name="""id""", type="string", length=32)
should be
#ORM\Column(name="id", type="string", length=32)
and so on
I'm trying to create a form which dinamically load all "sites" related to a "project", It seems like this would be of use, so I tried it:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class EngineeringType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('project','entity',array(
'class' => 'tBundle:Project',
'label' => 'Project'
;
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$sites = $data->getProject()->getSites();
$form->add('site', 'entity', array('choices' => $sites));
}
);
}
My problem comes when I try to access the form, I get:
FatalErrorException: Error: Call to a member function getSites() on a non-object in ... tBundle\Form\EngineeringType.php line 41
Here are my entities:
namespace tBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Engineering
*
* #ORM\Table(name="engineerings")
* #ORM\Entity
*/
class Engineering
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="tBundle\Entity\Project")
* #ORM\JoinColumn(name="project_id", referencedColumnName="id",nullable=false)
*/
private $project;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set project
*
* #param string $project
* #return Engineering
*/
public function setProject(\tBundle\Entity\Project $project)
{
$this->project = $project;
return $this;
}
/**
* Get project
*
* #return string
*/
public function getProject()
{
return $this->project;
}
Project:
namespace tBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Project
*
* #ORM\Table(name="projects")
* #ORM\Entity
*/
class Project
{
/**
* #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="tBundle\Entity\Site")
* #ORM\JoinTable(name="project_sites",
* joinColumns={#ORM\JoinColumn(name="site_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="project_id", referencedColumnName="id")}
* )
*/
private $sites;
public function __construct()
{
$this->sites = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Project
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Get Sites
*
* #return array
*/
public function getSites()
{
return $this->sites;
}
/* Returns Project's Name */
public function __toString()
{
return $this->name;
}
What am I doing wrong?
EDIT
Controller:
/**
* Creates a form to create a Engineering entity.
*
* #param Engineering $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(Engineering $entity)
{
$form = $this->createForm(new EngineeringType(), $entity, array(
'action' => $this->generateUrl('engineering_create'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Create'));
return $form;
}
/**
* Creates a form to edit a Engineering entity.
*
* #param Engineering $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createEditForm(Engineering $entity)
{
$form = $this->createForm(new EngineeringType(), $entity, array(
'action' => $this->generateUrl('engineering_update', array('id' => $entity->getId())),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Update'));
return $form;
}
The PRE_SET_DATA event is actually fired twice. The first time will not have any data. There used to be a blurb in the manual explaining why but I could not find it again.
So just:
function(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
if ($data)
{
$sites = $data->getProject()->getSites();
$form->add('site', 'entity', array('choices' => $sites));
}
}
=======================================================
Updated answer to show how to handle non-existent $project:
if ($data)
{
$project = $data->getProject();
$sites = $project ? $project->getSites() : array();
$form->add('site', 'entity', array('choices' => $sites));
}
There is an easy solution, why don't you use the "property" property on the form builder?
$builder
->add('project','entity',array(
'class' => 'tBundle:Project',
'label' => 'Project',
'property' => 'sites');
Or even you can use a query builder if this is not enough:
$builder->add('users', 'entity', array(
'class' => 'AcmeHelloBundle:User',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')
->orderBy('u.username', 'ASC');
},
));
You can find here more description, if this is not enough:
Symfony documentation
EDIT:
So your problem that with the first solution, that it's going to be an array, so use my second option, and in the query builder, specify what will reflect your needs.
Or use it like the class is not Project but Sites.