Symfony2 error with forms and related entities - forms

I have been searching before for an answer for this problem but after hours I need to ask you.
I have one Entity called Pedidos, that have another entity related called PedidosMateriales, as you can see:
(...)
/**
* Pedidos
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="ochodoscuatro\IntranetBundle\Entity\PedidosRepository")
*/
class Pedidos
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
(...)
/**
* #ORM\OneToMany(targetEntity="PedidosMateriales", mappedBy="pedido")
*/
private $pedidosmateriales;
public function __construct() {
$this->pedidosmateriales = new \Doctrine\Common\Collections\ArrayCollection();
$this->pagos = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addPedidosmateriales(\ochodoscuatro\IntranetBundle\Entity\PedidosMateriales $pedidosmateriales){
$this->pedidosmateriales[] = $pedidosmateriales;
}
public function getPedidosmateriales(){
return $this->pedidosmateriales;
}
(...)
}
When I submit a form, if I get $_POST, I can see all the information good there, but just after doing
$form->handleRequest($request);
I get this error:
Neither the property "pedidosmateriales" nor one of the methods "addPedidosmaterial()", "addPedidosmateriale()", "setPedidosmateriales()", "__set()" or "__call()" exist and have public access in class "ochodoscuatro\IntranetBundle\Entity\Pedidos".
But I have them already written!
I've read that with "multiple" => true I could get the answer, but it launch another error telling that the option "multiple" does not exist.
My form is this:
$builder->add('fechapedido', 'date', array(//Fecha del pedido
'widget' => 'single_text',
'format' => 'dd/MM/yyyy',
'attr' => array('class' => 'datepicker'),
'data' => new \DateTime() //Valor (Fecha actual)
))
->add('cliente', 'entity', array(
'class' => 'ochodoscuatroIntranetBundle:Clientes',
'property' => 'nombre',
'empty_value' => 'Nombre cliente'))//Nombre del "placeholder"
->add('pedidosmateriales', 'collection', array('type' => new PedidosMaterialesType(), 'allow_add' => true, 'by_reference' => false,));
}

Related

Symfony : OrderBy in CollectionType

I have two tables (Player & Historique) that have a OneToMany association. In my PlayerType form, I have a CollectionType with an entry_type to my HistoriqueType. My question is about the order in which the data from HistoriqueType arrives. For the moment, it appears in ASC order of the id. But I would like it to appear in ASC order of the years (season).
Here are my two entities :
<?php
/**
* #ORM\Entity(repositoryClass=PlayerRepository::class)
*/
class Player
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity=Historique::class, mappedBy="player", cascade={"persist"}, orphanRemoval = true)
*/
public $playerHistoriques;
public function __construct()
{
$this->playerHistoriques = new ArrayCollection();
}
And my Historique Class :
<?php
/**
* #ORM\Entity(repositoryClass=HistoriqueRepository::class)
*/
class Historique
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Club::class, inversedBy="historiques")
* #ORM\JoinColumn(nullable=true)
*/
private $club;
/**
* #ORM\ManyToOne(targetEntity=Season::class, inversedBy="historiques")
* #ORM\JoinColumn(nullable=true)
* #ORM\OrderBy({"season" = "ASC"})
*/
private $season;
/**
* #ORM\ManyToOne(targetEntity=Player::class, inversedBy="playerHistoriques")
* #ORM\JoinColumn(nullable=true)
*/
private $player;
/**
* #ORM\ManyToOne(targetEntity=Position::class, inversedBy="historiques")
*/
private $position;
My PlayerType :
<?php
class PlayerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('playerHistoriques', CollectionType::class, [
'entry_type' => HistoriqueType::class,
'entry_options' => [
'label' => false
],
'by_reference' => false,
'allow_add' => true
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Player::class,
]);
}
}
And My HistoriqueType:
<?php
class HistoriqueType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('club', EntityType::class, [
'class' => Club::class,
'choice_label' => 'name',
'label' => false,
'required' => false,
'placeholder' => '-',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.name', 'ASC');
}
])
->add('season', EntityType::class, [
'class' => Season::class,
'choice_label' => 'year',
'label' => false,
'placeholder' => '-',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('s')
->orderBy('s.year', 'ASC');
}
])
->add('position', EntityType::class, [
'class' => Position::class,
'choice_label' => 'position',
'label' => false,
'placeholder' => '-',
'required' => false
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Historique::class,
'method' => 'get',
'csrf_protection' => false
]);
}
public function getBlockPrefix() {
return '';
}
}
In my edit form, I would like to order my collectionType by 'season' => 'ASC', in order to have the years in chronological order even in my edit form.
I have tried a query_builder like so in my PlayerType :
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('h')
->orderBy('h.season', 'ASC');
}
But it generated an error since collectionTypes cannot have queries in them.
I have tried to order it automatically in my PlayerEntity #ORM\OrderBy({"season" = "ASC"}) but it didn't work.
In your Historique Entity change the order by to year instead of season (I believe in your other form you are sorting by the year so hopefully this is the property you are looking to sort by).
/**
* #ORM\ManyToOne(targetEntity=Season::class, inversedBy="historiques")
* #ORM\JoinColumn(nullable=true)
* #ORM\OrderBy({"year" = "ASC"})
*/
private $season;
You should be able to remove the query builder now, unless you need to narrow the selection.
OrderBy must be in Palyer entity in OneToMany relation not in Historique
/**
*#ORM\OneToMany(targetEntity=Historique::class, mappedBy="player", cascade={"persist"}, orphanRemoval = true)
*#ORM\OrderBy({"season" = "ASC"})
*/
public $playerHistoriques;

Symfony CollectionType with "allow_add" not adding child entity to parent entity

On Symfony 2.8, I got the following entities:
Contact:
class Contact
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\Column
* #Assert\NotBlank
*/
protected $name;
/**
* #ORM\OneToMany(targetEntity="EmailContact", mappedBy="contact", cascade={"persist"})
* #Assert\Valid
*/
protected $emails;
// ...
/**
* Add emails
*
* #param \AppBundle\Entity\EmailContact $emails
* #return Contact
*/
public function addEmail(\AppBundle\Entity\EmailContact $emails)
{
$this->emails[] = $emails;
$emails->setContact($this); //this line added by me
return $this;
}
// ...
EmailContact:
class EmailContact
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\Column
* #Assert\NotBlank
*/
protected $email;
/**
* #ORM\ManyToOne(targetEntity="Contact", inversedBy="emails")
* #ORM\JoinColumn(nullable=false)
*/
protected $contact;
// ...
The rest of the methods were automatically generated by the doctrine:generate:entities command.
My forms are as follows:
ContactType:
class ContactType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', null, [
'label' => 'contact.name',
])
->add('emails', CollectionType::class, [
'label' => false,
'entry_options' => array('label' => false),
'entry_type' => EmailContactType::class,
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'prototype' => true,
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Contact'
]);
}
EmailContactType:
class EmailContactType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class, [
'label' => 'emailContact.email',
])
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\EmailContact'
]);
}
I do the javascript to add extra fields to the request, and submit it. Example request (from Symfony Profiler):
[
name => test4,
emails => [
0 => [
email => t#t.t4
],
1 => [
email => t#t.t5
]
],
_token => ...
]
But I get the following error:
An exception occurred while executing 'INSERT INTO email_contact ...
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'contact_id' cannot be null
Debugging, I see that the addEmail method above never gets called. What is happening here?
You missed by_reference => false in form collection definition
->add('emails', CollectionType::class, [
'label' => false,
'entry_options' => array('label' => false),
'entry_type' => EmailContactType::class,
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'prototype' => true,
'by_reference' => false; // <--- you missed this
]);
Take a look here
Your code should run as expected after this modification.
Moreover remember that if you have a setEmails() method inside Contact class, the framework end up to calling it and so you need (for each element of the collection) to set contact as well (as you're correctly doing in addEmails())

zf2 & doctrine2 ObjectRadio getting an variable criteria from the related entity

I'am trying to make a form for entity content with a OneToMany relation to URL. I want to display all the URL's in a radio input field.
so the form will look like this:
//an input field for content
url:
o url1
o url2
the user can choose one of the url's linked to the content to be the canonical url.
content entity
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\HasLifecycleCallbacks*
*/
class Content
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* SeoUrl's from page
*
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="Application\Entity\Url", mappedBy="content", cascade={"remove", "persist"})
*/
protected $urls;
URL entity
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Entity Class representing our Url module.
*
* #ORM\Entity
* #ORM\Table(name="url")
*/
class Url {
/**
* #ORM\Id
* #ORM\Column(type="integer");
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
protected $id;
/**
* Content with Url
*
* #ORM\ManyToOne(targetEntity="Application\Entity\Content", inversedBy="urls")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="content_id", referencedColumnName="id", onDelete="CASCADE")
* })
*/
protected $content;
/**
* #ORM\Column(type="string")
* #var string
*/
protected $url;
My formfield
namespace Content\Form\Fieldset;
use Content\Entity\Content;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
class ContentFieldset
{
protected $em;
public function __construct()
{
parent::__construct('content');
$this->setObject(new Content());
}
public function init()
{
$this->em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
$this->setHydrator(new DoctrineHydrator($this->em));
$this->add(
array(
'name' => 'id',
'type' => 'Zend\Form\Element\Hidden'
)
);
$this->add(
array(
'name' => 'url',
'type' => 'DoctrineModule\Form\Element\ObjectRadio',
'options' => array(
'label' => 'Url',
'object_manager' => $this->em,
'target_class' => 'Application\Entity\Url',
'property' => 'url',
'is_method' => true,
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array('content' => 1),// I need this to be the content id
'orderBy' => array(),
),
)
)
)
);
}
I want only the Url's that are related so i found find_method, but i can only use hard coded cricteria. My question is, how can i give the ObjectRadio form the id of content to search for in the url entity.
I tried to use $this->getObject->getId() , but no luck.
Side note: in the Url Entity there will be a field the hold the information of the url is canonical or not. And will be used to check it's ObjectRadio.
My form
$this->add(
array(
'name' => 'url',
'type' => 'Zend\Form\Element\Radio',
'options' => array(
'value_options' => array(
'' => ''
)
)
)
);
and then i overwrite the value_options in my view like this
$options = array();
$urls = $content->get('urls')->getFieldsets();
foreach($urls as $url) {
$url_id = $url->getObject()->getId();
$url_url = $url->getObject()->getUrl();
$options[$url_id] = $url_url;
}
echo $this->formRow($content->get('url')->setAttributes(
array(
'options' => $options
)
));
I hope someone in the future can use this answer.
Beter answers are allways welcome.

zend2 insert into collection/database

I have some problem with colection in zf2 and doctryne.
Problem: I add some client and need have some multiselect.
ClientEntity
/**
* #ORM\MappedSuperclass
*/
class Client {
...
/**
* #ORM\ManyToOne(targetEntity="\Module\Model\Job")
* #ORM\JoinColumn(name="job_id", referencedColumnName="id")
*/
protected $job;
/**
* #ORM\OneToMany(targetEntity="SomeOption", mappedBy="job", cascade= {"persist", "remove"})
* */
protected $someOption;
...
public function __construct() {
$this->someOption= new ArrayCollection();
}
OptionEntity
/**
* #ORM\MappedSuperclass
*/
class SomeOption{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="\Module\Model\Client")
* #ORM\JoinColumn(name="job_id", referencedColumnName="id")
* */
protected $job;
/**
* #ORM\Column(type="string", nullable=false)
*/
protected $option;
}
Bought model have getter and setter, in Client model have:
public function addSomeOption(Collection $options) {
foreach ($options as $option) {
$option->setJob($this);
$this->someOption->add($option);
}
return $this;
}
public function removeSomeOption(Collection $options) {
foreach ($options as $option) {
$option->setJob(null);
$this->someOption->removeElement($option);
}
return $this;
}
public function getSomeOption() {
return $this->someOption;
}
Form:
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'someOption',
'attributes' => array(
'id' => 'someOption',
),
'options' => array(
'label' => 'Rozliczenie',
'value_options' => array(
array('value' => '1r', 'label' => '1/rok'),
array('value' => '1m', 'label' => '1/miesiąc'),
array('value' => '1w', 'label' => '1/tydzień'),
array('value' => '1d', 'label' => '1/dzień'),
),
),
'attributes' => array(
'class' => 'span12 settlement chosen',
'multiple' => 'multiple'
)
));
after this i need have 1 row of client and 1+ row of someOption, may any help to repair code ? or much more will be explain what i make wrong.

Sonata Admin embed forms one-to-many does not persist

I've been trying to implement embed forms in Sonata Admin Bundle 2.0, Sonata User Bundle 2.0 and Symfony 2.0.16 (yes, I know it's kind of old right now) and after reading a lot of forum posts and manual I could be able to implement it... but just at form level, it can't display data in edition mode, or persist it in creation mode.
Being more expecific, I'm trying to make work a relationship between an User entity (from Sonata User Bundle) with an Email entity, in an one-to-many relationship (one User has many Emails, or just one). So in the User form is gonna have one or more emails forms dynamically embeded, which seems to be working, but are disconnected with the email table.
systemUser is pointing to the table user. I got to change it because I'm using PostgreSQL and word is reserved.
UserAdmin.php
<?php
class UserAdmin extends Admin
{
// more code
$formMapper
->with('General')
->add('username')
->add('email')
->add('plainPassword', 'text', array('required' => false))
->end()
->with('Groups')
->add('groups', 'sonata_type_model', array('required' => false))
->end()
->with('Profile')
->add('dateOfBirth', 'date', array('required' => false))
->add('firstname', null, array('required' => false))
->add('lastname', null, array('required' => false))
->add('website', 'url', array('required' => false))
->add('locale', null, array('required' => false))
->add('timezone', null, array('required' => false))
->end()
->with('Emails')
->add('emails', 'collection', array(
'label' => 'Emails',
'required' => true,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
),
array(
'edit' => 'inline',
'inline' => 'table',
'sortable' => 'id',
'targetEntity'=> 'MyProject\xBundle\Entity\Email',
'link_parameters' => array('context' => 'user'),
)
)
;
// more code ...
}
User.php
<?php
class User extends BaseUser
{
/**
* #var array emails
*/
protected $emails;
/**
* #var string user
*/
protected $user;
public function __construct()
{
$this->emails = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add emails
*
* #param MyProject\xBundle\Email $email
*/
public function addEmails(\MyProject\xBundle\Entity\Email $email)
{
$this->emails[] = $email;
}
/**
* Get emails
*
* #return Doctrine\Common\Collections\Collection
*/
public function getEmails()
{
return $this->emails;
}
/**
* Set emails
*
* #param $emails
* #return Email
*/
public function setEmails($emails)
{
$this->$emails = $emails;
foreach ($emails as $email) {
$email->setUser($this);
}
}
/**
*
* #param string $user
*/
public function setUser($user)
{
$this->user = $user;
}
}
Email.php
<?php
class Email
{
/**
* #var SystemUser
*
* #ORM\ManyToOne(targetEntity="User", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="system_user_id", referencedColumnName="id")
* })
*
*/
private $systemUser;
public function __construct()
{
$this->systemUser = new ArrayCollection();
}
/**
*
* #param MyProject\xBundle\Entity\User $systemUser
*/
public function setSystemUser(\MyProject\xBundle\Entity\User $systemUsers = null)
{
$this->systemUser = $systemUser;
return $this;
}
/**
* Get systemUser
*
* #return MyProject\xBundle\Entity\User
*/
public function getSystemUser()
{
return $this->systemUser;
}
}
Your approach needs a 'forward' relationship from the User to the Email entity, otherwise the admin doesn't know what to change and persist. Change your code in User.php so that the email variable actually knows about its relationship. That means you should add something like
/**
* #var array emails
* #ORM\OneToMany(targetEntity="Email", mappedBy="systemUser", cascade={"all"})
*/
protected $emails;