treeLeft, treeRight and treeLevel value not updated in Symfony 2 Tree - symfony

My symfony 2 project require to assign users to a company hierachy. So I try to use tree of StofDoctrineExtension bundle for this project. Here is my Entity class for "category":
<?php
namespace Application\Sonata\UserBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
/**
* #Gedmo\Tree(type="nested")
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
*/
class Category
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\Column(length=64)
*/
private $title;
/**
* #var integer
*
* #Gedmo\TreeLeft
* #ORM\Column(type="integer")
*/
private $treeLeft;
/**
* #var integer
*
* #Gedmo\TreeLevel
* #ORM\Column(type="integer")
*/
private $treeLevel;
/**
* #var integer
*
* #Gedmo\TreeRight
* #ORM\Column(type="integer")
*/
private $treeRight;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $parent;
/**
* #Gedmo\TreeRoot
* #ORM\Column(type="integer", nullable=true)
*/
private $root;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
*/
private $children;
/**
* #ORM\OneToMany(targetEntity="User", mappedBy="category")
*/
private $users;
public function __toString()
{
return ($this->title) ? $this->title : '单位';
}
/**
* Constructor
*/
public function __construct()
{
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Category
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Get treeLeft
*
* #return integer
*/
public function getTreeLeft()
{
return $this->treeLeft;
}
/**
* Get treeLevel
*
* #return integer
*/
public function getTreeLevel()
{
return $this->treeLevel;
}
/**
* Get treeRight
*
* #return integer
*/
public function getTreeRight()
{
return $this->treeRight;
}
/**
* Get root
*
* #return integer
*/
public function getRoot()
{
return $this->root;
}
/**
* Set parent
*
* #param \Application\Sonata\UserBundle\Entity\Category $parent
*
* #return Category
*/
public function setParent(\Application\Sonata\UserBundle\Entity\Category $parent = null)
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* #return \Application\Sonata\UserBundle\Entity\Category
*/
public function getParent()
{
return $this->parent;
}
/**
* Get children
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getChildren()
{
return $this->children;
}
/**
* Add user
*
* #param \Application\Sonata\UserBundle\Entity\User $user
*
* #return Category
*/
public function addUser(\Application\Sonata\UserBundle\Entity\User $user)
{
$this->users[] = $user;
return $this;
}
/**
* Remove user
*
* #param \Application\Sonata\UserBundle\Entity\User $user
*/
public function removeUser(\Application\Sonata\UserBundle\Entity\User $user)
{
$this->users->removeElement($user);
}
/**
* Get users
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getUsers()
{
return $this->users;
}
}
and my CategoryAdmin.php:
......
/**
* {#inheritdoc}
*/
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('title')
->add('parent', 'entity', array(
'class' => 'Application\Sonata\UserBundle\Entity\Category', // tree class
'query_builder' => function($er) {
return $er->createQueryBuilder('c')
->orderBy('c.treeLeft', 'ASC');
},
'required' => false,
))
;
}
When I created an category entity, only parent and root value are correctly set, treeLeft, treeRight and treeLevel are zero. I check the sample in the document, it did mention someting about eventlistener. Should I add an eventlestener for this? and How? Thanks for any help.

After I deleted all records and create new one, it works.

Related

Expected value of type "SourcingBundle\Entity\RequestForEstimate" for association field

I get the following error:
Expected value of type "SourcingBundle\Entity\RequestForEstimate" for
association field
"SourcingBundle\Entity\RequestForEstimateDetail#$detail", got
"Doctrine\Common\Collections\ArrayCollection" instead.
I'm trying to create a structure where there is a RequestForEstimate entity that may have multiple RequestForEstimateDetails nodes. Simply each row in the RequestForEstimateDetails will have a request for a different product and its details when RequestForEstimate only the common information. I'm new to Symfony and I struggle at the point where I want to save the parent element together with the children in the DB. I have been following this Symfony tutorial: How to Embed a Collection of Forms
Here is my code:
SourcingBundle\Entity\RequestForEstimate:
<?php
namespace SourcingBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* RequestForEstimate
*
* #ORM\Table(name="request_for_estimate")
* #ORM\Entity(repositoryClass="SourcingBundle\Repository\RequestForEstimateRepository")
*/
class RequestForEstimate
{
/**
* #var int
*
* #ORM\Column(name="request_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\Column(name="status", type="integer")
*/
private $status;
/**
* #var \DateTime
*
* #ORM\Column(name="create_time", type="datetime")
*/
private $createTime;
/**
* #var \DateTime
*
* #ORM\Column(name="update_time", type="datetime")
*/
private $updateTime;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="RequestForEstimateDetail", mappedBy="detail", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private $details;
/**
* Get id
*
* #return int
*/
/**
* Constructor
*/
public function __construct()
{
$this->details = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set status
*
* #param integer $status
*
* #return RequestForEstimate
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* #return integer
*/
public function getStatus()
{
return $this->status;
}
/**
* Set createTime
*
* #param \DateTime $createTime
*
* #return RequestForEstimate
*/
public function setCreateTime($createTime)
{
$this->createTime = $createTime;
return $this;
}
/**
* Get createTime
*
* #return \DateTime
*/
public function getCreateTime()
{
return $this->createTime;
}
/**
* Set updateTime
*
* #param \DateTime $updateTime
*
* #return RequestForEstimate
*/
public function setUpdateTime($updateTime)
{
$this->updateTime = $updateTime;
return $this;
}
/**
* Get updateTime
*
* #return \DateTime
*/
public function getUpdateTime()
{
return $this->updateTime;
}
/**
* Set name
*
* #param string $name
*
* #return RequestForEstimate
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Add detail
*
* #param \SourcingBundle\Entity\RequestForEstimateDetail $detail
*
* #return RequestForEstimate
*/
public function addDetail(\SourcingBundle\Entity\RequestForEstimateDetail $detail)
{
$this->details[] = $detail;
return $this;
}
/**
* Remove detail
*
* #param \SourcingBundle\Entity\RequestForEstimateDetail $detail
*/
public function removeDetail(\SourcingBundle\Entity\RequestForEstimateDetail $detail)
{
$this->details->removeElement($detail);
}
/**
* Get details
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getDetails()
{
return $this->details;
}
}
SourcingBundle\Entity\RequestForEstimateDetail:
<?php
namespace SourcingBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* RequestForEstimateDetail
*
* #ORM\Table(name="request_for_estimate_detail")
* #ORM\Entity(repositoryClass="SourcingBundle\Repository\RequestForEstimateDetailRepository")
*/
class RequestForEstimateDetail
{
/**
* #var int
*
* #ORM\Column(name="request_detail_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="RequestForEstimate", inversedBy="details")
* #ORM\JoinColumn(name="request_id", referencedColumnName="request_id")
*/
private $detail;
/**
* #var int
*
* #ORM\Column(name="product_id", type="integer")
*/
private $productId;
/**
* #var int
*
* #ORM\Column(name="quantity", type="integer")
*/
private $quantity;
/**
* #var string
*
* #ORM\Column(name="price_per_unit", type="decimal", precision=2, scale=0)
*/
private $pricePerUnit;
/**
* #var string
*
* #ORM\Column(name="shipping_cost", type="decimal", precision=2, scale=0)
*/
private $shippingCost;
/**
* #var string
*
* #ORM\Column(name="other_fees", type="decimal", precision=2, scale=0)
*/
private $otherFees;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set product
*
* #param integer $product
*
* #return RequestForEstimateDetail
*/
public function setProduct($product)
{
$this->product = $product;
return $this;
}
/**
* Get product
*
* #return int
*/
public function getProduct()
{
return $this->product;
}
/**
* Set quantity
*
* #param integer $quantity
*
* #return RequestForEstimateDetail
*/
public function setQuantity($quantity)
{
$this->quantity = $quantity;
return $this;
}
/**
* Get quantity
*
* #return int
*/
public function getQuantity()
{
return $this->quantity;
}
/**
* Set pricePerUnit
*
* #param string $pricePerUnit
*
* #return RequestForEstimateDetail
*/
public function setPricePerUnit($pricePerUnit)
{
$this->pricePerUnit = $pricePerUnit;
return $this;
}
/**
* Get pricePerUnit
*
* #return string
*/
public function getPricePerUnit()
{
return $this->pricePerUnit;
}
/**
* Set shippingCost
*
* #param string $shippingCost
*
* #return RequestForEstimateDetail
*/
public function setShippingCost($shippingCost)
{
$this->shippingCost = $shippingCost;
return $this;
}
/**
* Get shippingCost
*
* #return string
*/
public function getShippingCost()
{
return $this->shippingCost;
}
/**
* Set otherFees
*
* #param string $otherFees
*
* #return RequestForEstimateDetail
*/
public function setOtherFees($otherFees)
{
$this->otherFees = $otherFees;
return $this;
}
/**
* Get otherFees
*
* #return string
*/
public function getOtherFees()
{
return $this->otherFees;
}
/**
* Set requestId
*
* #param \SourcingBundle\Entity\RequestForEstimate $requestId
*
* #return RequestForEstimateDetail
*/
public function setRequestId(\SourcingBundle\Entity\RequestForEstimate $requestId = null)
{
$this->requestId = $requestId;
return $this;
}
/**
* Get requestId
*
* #return \SourcingBundle\Entity\RequestForEstimate
*/
public function getRequestId()
{
return $this->requestId;
}
/**
* Set productId
*
* #param \SourcingBundle\Entity\Product $productId
*
* #return RequestForEstimateDetail
*/
public function setProductId(\SourcingBundle\Entity\Product $productId = null)
{
$this->productId = $productId;
return $this;
}
/**
* Get productId
*
* #return \SourcingBundle\Entity\Product
*/
public function getProductId()
{
return $this->productId;
}
/**
* Set detail
*
* #param \SourcingBundle\Entity\RequestForEstimate $detail
*
* #return RequestForEstimateDetail
*/
public function setDetail(\SourcingBundle\Entity\RequestForEstimate $detail = null)
{
$this->detail = $detail;
return $this;
}
/**
* Get detail
*
* #return \SourcingBundle\Entity\RequestForEstimate
*/
public function getDetail()
{
return $this->detail;
}
/**
* Constructor
*/
public function __construct()
{
$this->detail = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add detail
*
* #param \SourcingBundle\Entity\RequestForEstimate $detail
*
* #return RequestForEstimateDetail
*/
public function addDetail(\SourcingBundle\Entity\RequestForEstimate $detail)
{
$this->detail[] = $detail;
return $this;
}
/**
* Remove detail
*
* #param \SourcingBundle\Entity\RequestForEstimate $detail
*/
public function removeDetail(\SourcingBundle\Entity\RequestForEstimate $detail)
{
$this->detail->removeElement($detail);
}
}
My controller:
<?php
namespace SourcingBundle\Controller;
use SourcingBundle\Entity\RequestForEstimate;
use SourcingBundle\Entity\RequestForEstimateDetail;
use SourcingBundle\Form\Type\RequestForEstimateType;
use SourcingBundle\Form\Type\RequestForEstimateDetailType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class RequestForEstimateController extends Controller
{
/**
* #Route("sourcing/request-for-estimate", name="request_for_estimate")
*/
public function addRequest(Request $request)
{
$RequestForEstimate = new RequestForEstimate();
$form = $this->createForm(RequestForEstimateType::class, $RequestForEstimate);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$request = $form->getData();
foreach($form->get('details')->getData() as $detail)
{
$request->addDetail($detail);
}
print_r($form->get('details')->getData());
$request->setStatus(0);
$request->setupdateTime(new \DateTime());
$request->setcreateTime(new \DateTime());
$em = $this->getDoctrine()->getManager();
$em->persist($request);
$em->flush();
return $this->redirectToRoute('RequestsForEstimate');
}
return $this->render('sourcing/requestforestimate/create.html.twig', array(
'form' => $form->createView(),
));
}
}
My forms:
<?php
namespace SourcingBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class RequestForEstimateType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('details', CollectionType::class, array(
'entry_type' => RequestForEstimateDetailType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
));
$builder->add('save', SubmitType::class, array('label' => 'Create Request'));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SourcingBundle\Entity\RequestForEstimate',
));
}
}
<?php
namespace SourcingBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class RequestForEstimateDetailType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('quantity');
$builder->add('pricePerUnit');
$builder->add('shippingCost');
$builder->add('otherFees');
// $builder->add('product');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'SourcingBundle\Entity\RequestForEstimateDetail',
));
}
}
In the RequestForEstimateDetail the getter/setter method of the field detail are for manage the collection itself (the add/remove methods are for the single element of the collection), so modify the getter/setter method to handle an ArrayCollection object.
As Example:
RequestForEstimateDetail
/**
* Set detail
*
* #param \Doctrine\Common\Collections\ArrayCollection an array of $detail
*
* #return RequestForEstimateDetail
*/
public function setDetail(\Doctrine\Common\Collections\ArrayCollection $detail = null)
{
$this->detail = $detail;
return $this;
}
/**
* Get detail
*
* #return \Doctrine\Common\Collections\ArrayCollection
*/
public function getDetail()
{
return $this->detail;
}
Hope this help
Well, the existing addDetail method in the RequestForEstimate entity did the trick:
public function addDetail(\SourcingBundle\Entity\RequestForEstimateDetail $detail)
{
$this->details->add($detail);
$detail->setDetail($this);
return $this;
}

Symfony + Sonata, OneToMany Relation

Could anyone give me a hand please?
I need to implement the list of books and readers using Sonata admin bundle.
The problem is that I get empty column of read books for each reader in the list.
Forein keys are written okay to the DB.
Please see the code:
ReadersAdmin Class
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name')
->add('book',
'entity',
array(
'class' => 'AppBundle:Books',
'property' => 'name',
'multiple' => true,
'expanded' => true
)
);
}
/**
* #param \Sonata\AdminBundle\Show\ShowMapper $showMapper
*
* #return void
*/
protected function configureShowField(ShowMapper $showMapper)
{
$showMapper
->add('name')
->add('book')
;
}
/**
* #param \Sonata\AdminBundle\Datagrid\ListMapper $listMapper
*
* #return void
*/
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('name')
->add('book')
->add('_action', 'input', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
)
))
;
}
ReadersRelations Entity
class ReadersRelations
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Books
*
* #ORM\ManyToOne(targetEntity="\AppBundle\Entity\Books", inversedBy="ReadersRelations")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $book;
/**
* #var Readers
*
* #ORM\ManyToOne(targetEntity="\AppBundle\Entity\Readers", inversedBy="ReadersRelations")
* #ORM\JoinColumn(name="reader_id", referencedColumnName="id")
*/
private $reader;
Books Entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Books
*
* #ORM\Entity
* #ORM\Table(name="Books")
*/
class Books
{
/**
* #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;
/**
* #var string
*
* #ORM\Column(name="author", type="string", length=255)
*
*/
private $author;
/**
* #var ReadersRelations
*
* #ORM\OneToMany(targetEntity="\AppBundle\Entity\ReadersRelations" , mappedBy="Books" , cascade={"all"})
*/
private $readers;
public function __toString()
{
return $this->name;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Books
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set author
*
* #param string $author
* #return Books
*/
public function setAuthor($author)
{
$this->author = $author;
return $this;
}
/**
* Get author
*
* #return string
*/
public function getAuthor()
{
return $this->author;
}
}
Readers Entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\ReadersRelations;
/**
* Readers
*
* #ORM\Entity
* #ORM\Table(name="Readers")
*/
class Readers
{
/**
* #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;
/**
* #var ReadersRelations
*
* #ORM\OneToMany(targetEntity="\AppBundle\Entity\ReadersRelations" , mappedBy="Readers" , cascade={"all"})
*/
private $book;
private $books;
public function __construct()
{
$this->book = new ArrayCollection();
$this->books = new ArrayCollection();
}
public function __toString()
{
return $this->name;
}
public function getBook()
{
$books = new ArrayCollection();
foreach($books as $p)
{
$books[] = $p->getBook();
}
return $books;
}
public function setBook($books)
{
foreach($books as $p)
{
$po = new ReadersRelations();
$po->setBook($p);
$po->setReader($this);
$this->addPo($po);
}
}
public function addPo($ProductOrder)
{
$this->book[] = $ProductOrder;
}
public function removePo($ProductOrder)
{
return $this->book->removeElement($ProductOrder);
}
class Readers
{
/**
* #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;
/**
* #var ReadersRelations
*
* #ORM\OneToMany(targetEntity="\AppBundle\Entity\ReadersRelations" , mappedBy="Readers" , cascade={"all"})
*/
private $book;
private $books;
public function __construct()
{
$this->book = new ArrayCollection();
$this->books = new ArrayCollection();
}
public function __toString()
{
return $this->name;
}
public function getBook()
{
$books = new ArrayCollection();
foreach($books as $p)
{
$books[] = $p->getBook();
}
return $books;
}
public function setBook($books)
{
foreach($books as $p)
{
$po = new ReadersRelations();
$po->setBook($p);
$po->setReader($this);
$this->addPo($po);
}
}
public function addPo($ProductOrder)
{
$this->book[] = $ProductOrder;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Readers
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
}
So, have you problem for store data using your relationship ? Or juste for get data in list ?
If it's for save data, try to implement Sonata type collection or Sonata type model field types for register new related entries.
Then, if you have any problems, we will check if your entities setters have missing actions.

symfony2: one-to-many relationship and embeded forms

I have these 3 entities below. Keep in mind that Pedido is like Order/Cart, and Subitem is like Product.
Pedido:
namespace Project\FrontendBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
//use Gedmo\Mapping\Annotation as Gedmo;
/**
* Project\FrontendBundle\Entity\Pedido
*
* #ORM\Table
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Pedido
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="pedidos")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
**/
private $user;
/**
* #ORM\OneToMany(targetEntity="PedidoSubitem", mappedBy="pedido", cascade={"persist", "remove"})
**/
private $pedidoSubitems;
/**
* #ORM\Column(type="float", name="subtotal")
*/
private $subtotal;
/**
* #ORM\Column(type="float", name="iva")
*/
private $iva;
/**
* #ORM\Column(type="float", name="total")
*/
private $total;
/**
* #ORM\Column(type="text", length=5000, name="notas", nullable=true)
*
* #var text $notas
*/
private $notas;
/**
* #var \DateTime $created
*
* #ORM\Column(type="datetime")
*/
private $created;
/**
* #var \DateTime $updated
*
* #ORM\Column(type="datetime", nullable=true)
*/
private $updated;
/**
* #var \DateTime $contentChanged
*
* #ORM\Column(name="content_changed", type="datetime", nullable=true)
*/
private $contentChanged;
public function __construct() {
$this->pedidoSubitems = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set cliente
*
* #param \Project\FrontendBundle\Entity\Cliente $cliente
* #return Pedido
*/
public function setCliente(\Project\FrontendBundle\Entity\Cliente $cliente = null)
{
$this->cliente = $cliente;
return $this;
}
/**
* Get cliente
*
* #return \Project\FrontendBundle\Entity\Cliente
*/
public function getCliente()
{
return $this->cliente;
}
/**
* Add pedidoSubitems
*
* #param \Project\FrontendBundle\Entity\PedidoSubitem $pedidoSubitems
* #return Pedido
*/
public function addPedidoSubitem(\Project\FrontendBundle\Entity\PedidoSubitem $pedidoSubitems)
{
$this->pedidoSubitems[] = $pedidoSubitems;
return $this;
}
public function addPedido(\Project\FrontendBundle\Entity\Pedido $pedido)
{
$this->pedidoSubitems[] = $pedido;
}
/**
* Remove pedidoSubitems
*
* #param \Project\FrontendBundle\Entity\PedidoSubitem $pedidoSubitems
*/
public function removePedidoSubitem(\Project\FrontendBundle\Entity\PedidoSubitem $pedidoSubitems)
{
$this->pedidoSubitems->removeElement($pedidoSubitems);
}
/**
* Get pedidoSubitems
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPedidoSubitems()
{
return $this->pedidoSubitems;
}
/**
* Set user
*
* #param \Project\FrontendBundle\Entity\User $user
* #return Pedido
*/
public function setUser(\Project\FrontendBundle\Entity\User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \Project\FrontendBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Set notas
*
* #param string $notas
* #return Pedido
*/
public function setNotas($notas)
{
$this->notas = $notas;
return $this;
}
/**
* Get notas
*
* #return string
*/
public function getNotas()
{
return $this->notas;
}
/**
* Set created
*
* #param \DateTime $created
* #return Pedido
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* #param \DateTime $updated
* #return Pedido
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* #return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set contentChanged
*
* #param \DateTime $contentChanged
* #return Pedido
*/
public function setContentChanged($contentChanged)
{
$this->contentChanged = $contentChanged;
return $this;
}
/**
* Get contentChanged
*
* #return \DateTime
*/
public function getContentChanged()
{
return $this->contentChanged;
}
/**
* #ORM\PrePersist
*/
public function doStuffOnPrePersist()
{
$this->created = new \DateTime("now");
}
/**
* #ORM\PreUpdate
*/
public function doStuffOnPreUpdate()
{
$this->updated = new \DateTime("now");
}
/**
* Set subtotal
*
* #param float $subtotal
* #return Pedido
*/
public function setSubtotal($subtotal)
{
$this->subtotal = $subtotal;
return $this;
}
/**
* Get subtotal
*
* #return float
*/
public function getSubtotal()
{
return $this->subtotal;
}
/**
* Set iva
*
* #param float $iva
* #return Pedido
*/
public function setIva($iva)
{
$this->iva = $iva;
return $this;
}
/**
* Get iva
*
* #return float
*/
public function getIva()
{
return $this->iva;
}
/**
* Set total
*
* #param float $total
* #return Pedido
*/
public function setTotal($total)
{
$this->total = $total;
return $this;
}
/**
* Get total
*
* #return float
*/
public function getTotal()
{
return $this->total;
}
}
PedidoSubitem:
namespace Project\FrontendBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Project\FrontendBundle\Entity\PedidoSubitem
*
* #ORM\Table
* #ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
*/
class PedidoSubitem
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Pedido", inversedBy="PedidoSubitems", cascade={"persist"})
* #ORM\JoinColumn(name="pedido_id", referencedColumnName="id")
**/
private $pedido;
/**
* #ORM\ManyToOne(targetEntity="Subitem", inversedBy="PedidoSubitems", cascade={"persist"})
* #ORM\JoinColumn(name="subitem_id", referencedColumnName="id")
**/
private $subitem;
/**
* #ORM\Column(type="integer", name="number", nullable=false)
*
* #var integer $number
*/
protected $number;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set pedido
*
* #param \Project\FrontendBundle\Entity\Pedido $pedido
* #return PedidoSubitem
*/
public function setPedido(\Project\FrontendBundle\Entity\Pedido $pedido = null)
{
$this->pedido = $pedido;
return $this;
}
/**
* Get pedido
*
* #return \Project\FrontendBundle\Entity\Pedido
*/
public function getPedido()
{
return $this->pedido;
}
/**
* Set subitem
*
* #param \Project\FrontendBundle\Entity\Subitem $subitem
* #return PedidoSubitem
*/
public function setSubitem(\Project\FrontendBundle\Entity\Subitem $subitem = null)
{
$this->subitem = $subitem;
return $this;
}
/**
* Get subitem
*
* #return \Project\FrontendBundle\Entity\Subitem
*/
public function getSubitem()
{
return $this->subitem;
}
/**
* Set integer
*
* #param integer $integer
* #return PedidoSubitem
*/
public function setInteger($integer)
{
$this->integer = $integer;
return $this;
}
/**
* Get integer
*
* #return integer
*/
public function getInteger()
{
return $this->integer;
}
/**
* Set number
*
* #param integer $number
* #return PedidoSubitem
*/
public function setNumber($number)
{
$this->number = $number;
return $this;
}
/**
* Get number
*
* #return integer
*/
public function getNumber()
{
return $this->number;
}
}
Subitem:
namespace Project\FrontendBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
//use Vich\UploaderBundle\Mapping\Annotation as Vich;
//use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Project\FrontendBundle\Entity\Subitem
*
* #ORM\Table
* #ORM\Entity
*/
class Subitem
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255, name="nombre")
*/
protected $nombre;
/**
* #ORM\OneToMany(targetEntity="PedidoSubitem", mappedBy="subitem")
**/
private $pedidoSubitems;
/**
* #var integer $position
*
* #ORM\Column(name="position", type="integer")
*/
private $position;
/**
* #ORM\Column(type="string", length=255, name="image_name", nullable=true)
*
* #var string $imageName
*/
protected $imageName;
/**
* #ORM\Column(type="boolean", name="activado", nullable=true)
*
* #var boolean $activado
*/
protected $activado = true;
/**
* #ORM\Column(type="datetime", nullable=true)
*
* #var \DateTime $updatedAt
*/
protected $updatedAt;
/**
* #ORM\Column(type="float", scale=2, name="precio", nullable=true)
*
* #var string $precio
*/
protected $precio;
/**
* #ORM\Column(length=128, unique=true)
*/
private $slug;
public function __construct() {
$this->products = new ArrayCollection();
$this->pedidoSubitems = new ArrayCollection();
}
public function __toString()
{
return $this->nombre;
}
public function getSlug()
{
return $this->slug;
}
/**
* Set position
*
* #param integer $position
* #return Subitem
*/
public function setPosition($position)
{
$this->position = $position;
return $this;
}
/**
* Get position
*
* #return integer
*/
public function getPosition()
{
return $this->position;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set tipo
*
* #param boolean $tipo
* #return Product
*/
public function setTipo($tipo)
{
$this->tipo = $tipo;
return $this;
}
/**
* Set imageName
*
* #param string $imageName
* #return Subitem
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
return $this;
}
/**
* Get imageName
*
* #return string
*/
public function getImageName()
{
return $this->imageName;
}
public function getImage()
{
return $this->image;
}
public function setImage($image)
{
$this->image = $image;
if($this->image) {
$this->updatedAt = new \DateTime('now');
}
}
/**
* Set updatedAt
*
* #param \DateTime $updatedAt
* #return Product
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* #return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* Set nombre
*
* #param string $nombre
* #return Subitem
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
return $this;
}
/**
* Get nombre
*
* #return string
*/
public function getNombre()
{
return $this->nombre;
}
/**
* Set slug
*
* #param string $slug
* #return Subitem
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Set activado
*
* #param boolean $activado
* #return Subitem
*/
public function setActivado($activado)
{
$this->activado = $activado;
return $this;
}
/**
* Get activado
*
* #return boolean
*/
public function getActivado()
{
return $this->activado;
}
/**
* Set item
*
* #param \Project\FrontendBundle\Entity\Item $item
* #return Subitem
*/
public function setItem(\Project\FrontendBundle\Entity\Item $item)
{
$this->item = $item;
return $this;
}
/**
* Get item
*
* #return \Project\FrontendBundle\Entity\Item
*/
public function getItem()
{
return $this->item;
}
/**
* Add pedidos
*
* #param \Project\FrontendBundle\Entity\Pedido $pedidos
* #return Subitem
*/
public function addPedido(\Project\FrontendBundle\Entity\Pedido $pedidos)
{
$this->pedidos[] = $pedidos;
return $this;
}
/**
* Remove pedidos
*
* #param \Project\FrontendBundle\Entity\Pedido $pedidos
*/
public function removePedido(\Project\FrontendBundle\Entity\Pedido $pedidos)
{
$this->pedidos->removeElement($pedidos);
}
/**
* Get pedidos
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPedidos()
{
return $this->pedidos;
}
/**
* Set precio
*
* #param float $precio
* #return Subitem
*/
public function setPrecio($precio)
{
$this->precio = $precio;
return $this;
}
/**
* Get precio
*
* #return float
*/
public function getPrecio()
{
return $this->precio;
}
/**
* Add subitems
*
* #param \Project\FrontendBundle\Entity\PedidoSubitem $subitems
* #return Subitem
*/
public function addSubitem(\Project\FrontendBundle\Entity\PedidoSubitem $subitems)
{
$this->subitems[] = $subitems;
return $this;
}
/**
* Remove subitems
*
* #param \Project\FrontendBundle\Entity\PedidoSubitem $subitems
*/
public function removeSubitem(\Project\FrontendBundle\Entity\PedidoSubitem $subitems)
{
$this->subitems->removeElement($subitems);
}
/**
* Get subitems
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getSubitems()
{
return $this->subitems;
}
/**
* Add pedidoSubitems
*
* #param \Project\FrontendBundle\Entity\PedidoSubitem $pedidoSubitems
* #return Subitem
*/
public function addPedidoSubitem(\Project\FrontendBundle\Entity\PedidoSubitem $pedidoSubitems)
{
$this->pedidoSubitems[] = $pedidoSubitems;
return $this;
}
/**
* Remove pedidoSubitems
*
* #param \Project\FrontendBundle\Entity\PedidoSubitem $pedidoSubitems
*/
public function removePedidoSubitem(\Project\FrontendBundle\Entity\PedidoSubitem $pedidoSubitems)
{
$this->pedidoSubitems->removeElement($pedidoSubitems);
}
/**
* Get pedidoSubitems
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPedidoSubitems()
{
return $this->pedidoSubitems;
}
}
And this type clases:
PedidoType:
namespace Project\FrontendBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Project\FrontendBundle\Form\PedidoSubitemType;
class PedidoType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user')
//->add('pedidoSubitem', 'collection', array('type' => new PedidoSubitemType()))
->add('pedidoSubitems', new PedidoSubitemType())
->add('subtotal')
->add('iva')
->add('total')
->add('notas')
//->add('created')
//->add('updated')
//->add('contentChanged')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Project\FrontendBundle\Entity\Pedido'
));
}
/**
* #return string
*/
public function getName()
{
return 'project_frontendbundle_pedido';
}
}
PedidoSubitemType:
namespace Project\FrontendBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class PedidoSubitemType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('number', 'text')
//->add('pedido')
->add('subitem', 'entity', array('class' => 'ProjectFrontendBundle:Subitem'))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Project\FrontendBundle\Entity\PedidoSubitem'
));
}
/**
* #return string
*/
public function getName()
{
return 'project_frontendbundle_pedidosubitem';
}
}
I would like to show a form that creates a Pedido form with PedidoSubitem forms embeded.
As you can see in PedidoSubitem I have tried these two codes:
Case A
$builder
->add('user')
->add('pedidoSubitems', new PedidoSubitemType())
The fields related to PedidoSubitemType are shown correctly, but after submitting the form, I get:
Neither the property pedidoSubitems nor one of the methods
addPedidoSubitem(), removePedidoSubitem(), setPedidoSubitems(),
pedidoSubitems(), __set() or __call() exist and have public access in
class "Project\FrontendBundle\Entity\Pedido". (500 Internal
Server Error)
Case B
$builder
->add('user')
->add('pedidoSubitem', 'collection', array('type' => new PedidoSubitemType()))
But when rendering the form ({{ form(form) }}), the fields of PedidoSubitemType are not shown, but just this:
<div>
<label class="required">Pedido subitem</label>
<div id="project_frontendbundle_pedido_pedidoSubitem"></div>
</div>
You need to use Case A, that you've described above but rename field to pedidoSubitem. I mean:
$builder->add('pedidoSubitem', new PedidoSubitemType());
And probably you should rename entity's method addPedido() to addPedidoSubitem().

Why Symfony and Doctrine uses __toString instead of getId when inserting/updateing record

I have a form with 4 realtionships but have a problem with 2 of them.
Here is code for my form:
<?php
namespace Psw\AdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class MeatType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name_pl')
->add('name_en')
->add('jm')
->add('vat')
->add('net_price')
->add('min')
->add('new')
->add('promotion', 'entity', array(
'class' => 'PswAdminBundle:Promotions',
'empty_value' => 'No promotion',
'required' => false
))
->add('promotion_price')
->add('producer', 'entity', array(
'class' => 'PswAdminBundle:MeatProducers'
))
->add('animals')
->add('categories')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Psw\AdminBundle\Entity\Meat'
));
}
public function getName()
{
return 'psw_adminbundle_meattype';
}
}
And entities
Meat:
<?php
namespace Psw\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Meat
*
* #ORM\Table()
* #ORM\Entity
*/
class Meat
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name_pl", type="string", length=255)
*/
private $name_pl;
/**
* #var string
*
* #ORM\Column(name="name_en", type="string", length=255)
*/
private $name_en;
/**
* #var string
*
* #ORM\Column(name="jm", type="string", length=10)
*/
private $jm;
/**
* #var integer
*
* #ORM\Column(name="vat", type="integer")
*/
private $vat;
/**
* #var float
*
* #ORM\Column(name="net_price", type="float")
*/
private $net_price;
/**
* #var float
*
* #ORM\Column(name="min", type="float")
*/
private $min;
/**
* #var boolean
*
* #ORM\Column(name="new", type="boolean")
*/
private $new;
/**
* #var integer
*
* #ORM\Column(name="promotion", type="integer", nullable=true)
* #ORM\ManyToOne(targetEntity="Promotions")
*/
private $promotion;
/**
* #var float
*
* #ORM\Column(name="promotion_price", type="float")
*
*/
private $promotion_price;
/**
* #ORM\ManyToMany(targetEntity="Animals")
* #ORM\JoinTable(name="meat_animals",
* joinColumns={#ORM\JoinColumn(name="meat_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="animal_id", referencedColumnName="id")}
* )
**/
private $animals;
/**
* #ORM\ManyToMany(targetEntity="MeatCategories")
* #ORM\JoinTable(name="meat_meat_categories",
* joinColumns={#ORM\JoinColumn(name="meat_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")}
* )
**/
private $categories;
/**
* #ORM\Column(name="producer", type="integer")
*
* #ORM\ManyToOne(targetEntity="MeatProducers", inversedBy="products")
* #ORM\JoinColumn(name="producer", referencedColumnName="id")
**/
private $producer;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name_pl
*
* #param string $namePl
* #return Meat
*/
public function setNamePl($namePl)
{
$this->name_pl = $namePl;
return $this;
}
/**
* Get name_pl
*
* #return string
*/
public function getNamePl()
{
return $this->name_pl;
}
/**
* Set name_en
*
* #param string $nameEn
* #return Meat
*/
public function setNameEn($nameEn)
{
$this->name_en = $nameEn;
return $this;
}
/**
* Get name_en
*
* #return string
*/
public function getNameEn()
{
return $this->name_en;
}
/**
* Set jm
*
* #param string $jm
* #return Meat
*/
public function setJm($jm)
{
$this->jm = $jm;
return $this;
}
/**
* Get jm
*
* #return string
*/
public function getJm()
{
return $this->jm;
}
/**
* Set vat
*
* #param integer $vat
* #return Meat
*/
public function setVat($vat)
{
$this->vat = $vat;
return $this;
}
/**
* Get vat
*
* #return integer
*/
public function getVat()
{
return $this->vat;
}
/**
* Set net_price
*
* #param float $netPrice
* #return Meat
*/
public function setNetPrice($netPrice)
{
$this->net_price = $netPrice;
return $this;
}
/**
* Get net_price
*
* #return float
*/
public function getNetPrice()
{
return $this->net_price;
}
/**
* Set min
*
* #param float $min
* #return Meat
*/
public function setMin($min)
{
$this->min = $min;
return $this;
}
/**
* Get min
*
* #return float
*/
public function getMin()
{
return $this->min;
}
/**
* Set new
*
* #param boolean $new
* #return Meat
*/
public function setNew($new)
{
$this->new = $new;
return $this;
}
/**
* Get new
*
* #return boolean
*/
public function getNew()
{
return $this->new;
}
/**
* Set promotion
*
* #param integer $promotion
* #return Meat
*/
public function setPromotion($promotion)
{
$this->promotion = $promotion;
return $this;
}
/**
* Get promotion
*
* #return integer
*/
public function getPromotion()
{
return $this->promotion;
}
/**
* Constructor
*/
public function __construct()
{
$this->animals = new ArrayCollection();
$this->categories = new ArrayCollection();
}
/**
* Add animals
*
* #param \Psw\AdminBundle\Entity\Animals $animals
* #return Meat
*/
public function addAnimal(\Psw\AdminBundle\Entity\Animals $animals)
{
$this->animals[] = $animals;
return $this;
}
/**
* Remove animals
*
* #param \Psw\AdminBundle\Entity\Animals $animals
*/
public function removeAnimal(\Psw\AdminBundle\Entity\Animals $animals)
{
$this->animals->removeElement($animals);
}
/**
* Get animals
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAnimals()
{
return $this->animals;
}
/**
* Add categories
*
* #param \Psw\AdminBundle\Entity\Meat_categories $categories
* #return Meat
*/
public function addCategorie(\Psw\AdminBundle\Entity\Meat_categories $categories)
{
$this->categories[] = $categories;
return $this;
}
/**
* Remove categories
*
* #param \Psw\AdminBundle\Entity\Meat_categories $categories
*/
public function removeCategorie(\Psw\AdminBundle\Entity\Meat_categories $categories)
{
$this->categories->removeElement($categories);
}
/**
* Get categories
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
/**
* Set producer
*
* #param \Psw\AdminBundle\Entity\MeatProducers $producer
* #return Meat
*/
public function setProducer(\Psw\AdminBundle\Entity\MeatProducers $producer = null)
{
$this->producer = $producer;
return $this;
}
/**
* Get producer
*
* #return \Psw\AdminBundle\Entity\MeatProducers
*/
public function getProducer()
{
return $this->producer;
}
/**
* Set promotion_price
*
* #param float $promotionPrice
* #return Meat
*/
public function setPromotionPrice($promotionPrice)
{
$this->promotion_price = $promotionPrice;
return $this;
}
/**
* Get promotion_price
*
* #return float
*/
public function getPromotionPrice()
{
return $this->promotion_price;
}
}
MeatProducers:
<?php
namespace Psw\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* MeatProducers
*
* #ORM\Table()
* #ORM\Entity
*/
class MeatProducers
{
/**
* #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\OneToMany(targetEntity="Meat", mappedBy="producer")
**/
private $products;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return MeatProducers
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
public function __toString()
{
return $this->name;
}
/**
* Constructor
*/
public function __construct()
{
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add products
*
* #param \Psw\AdminBundle\Entity\Meat $products
* #return MeatProducers
*/
public function addProduct(\Psw\AdminBundle\Entity\Meat $products)
{
$this->products[] = $products;
return $this;
}
/**
* Remove products
*
* #param \Psw\AdminBundle\Entity\Meat $products
*/
public function removeProduct(\Psw\AdminBundle\Entity\Meat $products)
{
$this->products->removeElement($products);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
There are no problems with many to many raltions on Animals and Categories.
But Promotions and Producers where i have many to one cause some issues.
Problem is that when trying to insert/update record doctrine calls __toString method instead of getId to obtain value to put into database.
I have working example like this with other entities, they are almost the same but in this case it just does not want to work.
Controller code is from crud generator, I didn't change anything there if necessary I may post it.
Question is how to make it use getId method?
The issue here is not with the __toString, it lies in your mapping annotation. You should not use #Column for association mappings. Instead, use #JoinColumn:
/**
* #ORM\ManyToOne(targetEntity="Promotions")
* #ORM\JoinColumn(name="promotion_id", referencedColumnName="id")
*/
private $promotion;

Form nulling a value when user doesn't supply it

Reading this page I've setup a form to handle PATCH requests.
I've a Player entity:
<?php
namespace Acme\PlayerBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\PlayerBundle\Entity\Player
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Acme\PlayerBundle\Entity\PlayerRepository")
*/
class Player
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="players")
* #ORM\JoinColumn(nullable=false)
*/
private $owner;
/**
* #ORM\ManyToOne(targetEntity="Acme\TeamBundle\Entity\Team", inversedBy="players")
* #ORM\JoinColumn(nullable=false)
* #Assert\NotBlank()
*/
private $team;
/**
* #var integer $shirtNumber
*
* #ORM\Column(name="shirtNumber", type="smallint")
* #Assert\NotBlank()
*/
private $shirtNumber;
/**
* #var integer $vsid
*
* #ORM\Column(name="vsid", type="integer", nullable=true)
*/
private $vsid;
/**
* #var string $firstname
*
* #ORM\Column(name="firstname", type="string", length=255, nullable=true)
*/
private $firstname;
/**
* #var string $lastname
*
* #ORM\Column(name="lastname", type="string", length=255, nullable=true)
*/
private $lastname;
/**
* #var boolean $deleted
*
* #ORM\Column(name="deleted", type="boolean")
*/
private $deleted = false;
/**
* #var integer $role
*
* #ORM\Column(type="integer", nullable=true)
*/
private $role;
/**
* Create the user salt
*/
public function __construct()
{
//TODO: just for test
$this->uniqueId = substr(uniqid(), 0, 14);
}
/* MANAGED BY DOCTRINE, DON'T EDIT */
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Player
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set shirtNumber
*
* #param integer $shirtNumber
* #return Player
*/
public function setShirtNumber($shirtNumber)
{
$this->shirtNumber = $shirtNumber;
return $this;
}
/**
* Get shirtNumber
*
* #return integer
*/
public function getShirtNumber()
{
return $this->shirtNumber;
}
/**
* Set vsid
*
* #param integer $vsid
* #return Player
*/
public function setVsid($vsid)
{
$this->vsid = $vsid;
return $this;
}
/**
* Get vsid
*
* #return integer
*/
public function getVsid()
{
return $this->vsid;
}
/**
* Set firstname
*
* #param string $firstname
* #return Player
*/
public function setFirstname($firstname)
{
$this->firstname = $firstname;
return $this;
}
/**
* Get firstname
*
* #return string
*/
public function getFirstname()
{
return $this->firstname;
}
/**
* Set lastname
*
* #param string $lastname
* #return Player
*/
public function setLastname($lastname)
{
$this->lastname = $lastname;
return $this;
}
/**
* Get lastname
*
* #return string
*/
public function getLastname()
{
return $this->lastname;
}
/**
* Set deleted
*
* #param boolean $deleted
* #return Player
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
/**
* Get deleted
*
* #return boolean
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* Set role
*
* #param integer $role
* #return Player
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* Get role
*
* #return integer
*/
public function getRole()
{
return $this->role;
}
/**
* Set owner
*
* #param Acme\UserBundle\Entity\User $owner
* #return Player
*/
public function setOwner(\Acme\UserBundle\Entity\User $owner)
{
$this->owner = $owner;
return $this;
}
/**
* Get owner
*
* #return Acme\UserBundle\Entity\User
*/
public function getOwner()
{
return $this->owner;
}
/**
* Set team
*
* #param Acme\TeamBundle\Entity\Team $team
* #return Player
*/
public function setTeam(\Acme\TeamBundle\Entity\Team $team)
{
$this->team = $team;
return $this;
}
/**
* Get team
*
* #return Acme\TeamBundle\Entity\Team
*/
public function getTeam()
{
return $this->team;
}
}
and a Team entity:
<?php
namespace Acme\TeamBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\TeamBundle\Entity\Team
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Acme\TeamBundle\Entity\TeamRepository")
*/
class Team
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $uniqueId
*
* #ORM\Column(name="uniqueId", type="string", length=15)
*/
private $uniqueId;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="teams")
* #ORM\JoinColumn(nullable=false)
*/
private $owner;
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=50)
* #Assert\NotBlank()
*/
private $name;
/**
* #var string $homeColor
*
* #ORM\Column(name="homeColor", type="string", length=7, nullable=true)
*/
private $homeColor;
/**
* #var string $awayColor
*
* #ORM\Column(name="awayColor", type="string", length=7, nullable=true)
*/
private $awayColor;
/**
* #var string $homeShirt
*
* #ORM\Column(name="homeShirt", type="string", length=50, nullable=true)
*/
private $homeShirt;
/**
* #var string $awayShirt
*
* #ORM\Column(name="awayShirt", type="string", length=50, nullable=true)
*/
private $awayShirt;
/**
* #var string $teamLogo
*
* #ORM\Column(name="teamLogo", type="string", length=50, nullable=true)
*/
private $teamLogo;
/**
* #var boolean $deleted
*
* #ORM\Column(name="deleted", type="boolean")
*/
private $deleted = false;
/**
* #ORM\OneToMany(targetEntity="Acme\PlayerBundle\Entity\Player", mappedBy="team", cascade={"persist", "remove"})
*/
private $players;
/**
* #ORM\OneToMany(targetEntity="Acme\MatchBundle\Entity\Match", mappedBy="homeTeam")
*/
private $homeMatches;
/**
* #ORM\OneToMany(targetEntity="Acme\MatchBundle\Entity\Match", mappedBy="awayTeam")
*/
private $awayMatches;
/**
* Create the user salt
*/
public function __construct()
{
$this->players = new ArrayCollection();
$this->homeMatches = new ArrayCollection();
$this->awayMatches = new ArrayCollection();
//TODO: just for test
$this->uniqueId = substr(uniqid(), 0, 14);
}
public function getMatches()
{
return array_merge($this->awayMatches->toArray(), $this->homeMatches->toArray());
}
/* MANAGED BY DOCTRINE, DON'T EDIT */
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set uniqueId
*
* #param string $uniqueId
* #return Team
*/
public function setUniqueId($uniqueId)
{
$this->uniqueId = $uniqueId;
return $this;
}
/**
* Get uniqueId
*
* #return string
*/
public function getUniqueId()
{
return $this->uniqueId;
}
/**
* Set name
*
* #param string $name
* #return Team
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set homeColor
*
* #param string $homeColor
* #return Team
*/
public function setHomeColor($homeColor)
{
$this->homeColor = $homeColor;
return $this;
}
/**
* Get homeColor
*
* #return string
*/
public function getHomeColor()
{
return $this->homeColor;
}
/**
* Set awayColor
*
* #param string $awayColor
* #return Team
*/
public function setAwayColor($awayColor)
{
$this->awayColor = $awayColor;
return $this;
}
/**
* Get awayColor
*
* #return string
*/
public function getAwayColor()
{
return $this->awayColor;
}
/**
* Set homeShirt
*
* #param string $homeShirt
* #return Team
*/
public function setHomeShirt($homeShirt)
{
$this->homeShirt = $homeShirt;
return $this;
}
/**
* Get homeShirt
*
* #return string
*/
public function getHomeShirt()
{
return $this->homeShirt;
}
/**
* Set awayShirt
*
* #param string $awayShirt
* #return Team
*/
public function setAwayShirt($awayShirt)
{
$this->awayShirt = $awayShirt;
return $this;
}
/**
* Get awayShirt
*
* #return string
*/
public function getAwayShirt()
{
return $this->awayShirt;
}
/**
* Set teamLogo
*
* #param string $teamLogo
* #return Team
*/
public function setTeamLogo($teamLogo)
{
$this->teamLogo = $teamLogo;
return $this;
}
/**
* Get teamLogo
*
* #return string
*/
public function getTeamLogo()
{
return $this->teamLogo;
}
/**
* Set deleted
*
* #param boolean $deleted
* #return Team
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
return $this;
}
/**
* Get deleted
*
* #return boolean
*/
public function getDeleted()
{
return $this->deleted;
}
/**
* Add players
*
* #param Acme\PlayerBundle\Entity\Player $players
* #return Team
*/
public function addPlayer(\Acme\PlayerBundle\Entity\Player $players)
{
$this->players[] = $players;
return $this;
}
/**
* Remove players
*
* #param Acme\PlayerBundle\Entity\Player $players
*/
public function removePlayer(\Acme\PlayerBundle\Entity\Player $players)
{
$this->players->removeElement($players);
}
/**
* Get players
*
* #return Doctrine\Common\Collections\Collection
*/
public function getPlayers()
{
return $this->players;
}
/**
* Add homeMatches
*
* #param Acme\MatchBundle\Entity\Match $homeMatches
* #return Team
*/
public function addHomeMatche(\Acme\MatchBundle\Entity\Match $homeMatches)
{
$this->homeMatches[] = $homeMatches;
return $this;
}
/**
* Remove homeMatches
*
* #param Acme\MatchBundle\Entity\Match $homeMatches
*/
public function removeHomeMatche(\Acme\MatchBundle\Entity\Match $homeMatches)
{
$this->homeMatches->removeElement($homeMatches);
}
/**
* Get homeMatches
*
* #return Doctrine\Common\Collections\Collection
*/
public function getHomeMatches()
{
return $this->homeMatches;
}
/**
* Add awayMatches
*
* #param Acme\MatchBundle\Entity\Match $awayMatches
* #return Team
*/
public function addAwayMatche(\Acme\MatchBundle\Entity\Match $awayMatches)
{
$this->awayMatches[] = $awayMatches;
return $this;
}
/**
* Remove awayMatches
*
* #param Acme\MatchBundle\Entity\Match $awayMatches
*/
public function removeAwayMatche(\Acme\MatchBundle\Entity\Match $awayMatches)
{
$this->awayMatches->removeElement($awayMatches);
}
/**
* Get awayMatches
*
* #return Doctrine\Common\Collections\Collection
*/
public function getAwayMatches()
{
return $this->awayMatches;
}
/**
* Set owner
*
* #param Acme\UserBundle\Entity\User $owner
* #return Team
*/
public function setOwner(\Acme\UserBundle\Entity\User $owner)
{
$this->owner = $owner;
return $this;
}
/**
* Get owner
*
* #return Acme\UserBundle\Entity\User
*/
public function getOwner()
{
return $this->owner;
}
}
Now I've created a player form class with the app/console and I've edited the team field to be an instance of the Team entity, this way:
<?php
namespace Acme\PlayerBundle\Form;
use Acme\TeamBundle\Entity\TeamRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class PlayerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('shirtNumber')
->add('firstname')
->add('lastname')
->add('role')
->add('team', 'entity', array(
'class' => 'AcmeTeamBundle:Team',
'query_builder' => function(TeamRepository $er) {
$query = $er->createQueryBuilder('t');
return $query;
}
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\PlayerBundle\Entity\Player',
'csrf_protection' => false
));
}
public function getName()
{
return 'player';
}
}
And this is the relevant part of my controller:
/**
* Create a new player
*
* #Route(".{_format}", name="api_player_create")
* #Method("POST")
* #ApiDoc(
* description="Create a new player",
* statusCodes={
* 201="Player created and informations are returned",
* 400="Missing informations",
* 403="The user isn't authorized"
* },
* input="Acme\PlayerBundle\Form\PlayerType",
* return="Acme\PlayerBundle\Entity\Player"
* )
*
* #return Renders the player just created
*/
public function createPlayerAction()
{
return $this->processForm(new Player());
}
/**
* Edit a player
*
* #param integer $id The id of the player to be created
*
* #Route("/{id}.{_format}", name="api_player_patch", requirements={ "id": "\d+" })
* #Method("PATCH")
* #ApiDoc(
* description="Edit a player",
* statusCodes={
* 200="Player is updated",
* 400="Missing informations",
* 403="The user isn't authorized"
* },
* input="Acme\PlayerBundle\Form\PlayerType",
* return="Acme\PlayerBundle\Entity\Player"
* )
*
* #return Renders the player just edited
*/
public function editPlayerAction(Player $player)
{
if ($player->getOwner() != $this->getUser()) {
throw new ApiException\PermissionDeniedException;
}
return $this->processForm($player);
}
/**
* Function to handle a form to create/edit a player
*
* #param Player $player The player to be created or edited
*
* #return Api Response
*/
private function processForm(Player $player)
{
/**
* Check if the player is new (to be created) or we're editing a player
*/
$statusCode = is_null($player->getId()) ? 201 : 200;
$form = $this->createForm(new PlayerType(), $player);
$form->bind($this->getRequest());
if ($form->isValid()) {
if($player->getTeam()->getOwner() != $this->getUser()) {
throw new ApiException\PermissionDeniedException;
}
$player->setOwner($this->getUser());
$this->entityManager->persist($player);
$this->entityManager->flush();
return $this->apiResponse->getResponse($player, $statusCode);
}
return $this->apiResponse->getResponse($form, 400, 'Missing arguments');
}
The player creation works fine, the player edit doesn't, when the user makes the api request, passing the ID in the url and the name of the player I get:
Catchable Fatal Error: Argument 1 passed to Acme\PlayerBundle\Entity\Player::setTeam() must be an instance of Acme\TeamBundle\Entity\Team, null given, called in /Volumes/Dati/Users/alessandro/Sites/acme-api/vendor/symfony/symfony/src/Symfony/Component/Form/Util/PropertyPath.php on line 538 and defined in /Volumes/Dati/Users/alessandro/Sites/acme-api/src/Acme/PlayerBundle/Entity/Player.php line 278
Seems that form is trying to set the Team to null, why?
I've tried both sending and not the team as the form parameters but it doesn't work.
Any clue?
Figured it out that null unsent fields in the form is the default behaviour in symfony.
There is an open request for partial form binding in symfony. For now a guy created a form event subscriber that adds the missing fields with the actual values:
https://gist.github.com/3720535
This is his code:
<?php
namespace Foo;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
/**
* Changes Form->bind() behavior so that it treats not set values as if they
* were sent unchanged.
*
* Use when you don't want fields to be set to NULL when they are not displayed
* on the page (or to implement PUT/PATCH requests).
*/
class PatchSubscriber implements EventSubscriberInterface
{
public function onPreBind(FormEvent $event)
{
$form = $event->getForm();
$clientData = $event->getData();
$clientData = array_replace($this->unbind($form), $clientData ?: array());
$event->setData($clientData);
}
/**
* Returns the form's data like $form->bind() expects it
*/
protected function unbind($form)
{
if ($form->hasChildren()) {
$ary = array();
foreach ($form->getChildren() as $name => $child) {
$ary[$name] = $this->unbind($child);
}
return $ary;
} else {
return $form->getClientData();
}
}
static public function getSubscribedEvents()
{
return array(
FormEvents::PRE_BIND => 'onPreBind',
);
}
}
to add to the form you have to edit the buildForm method in the form class:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$subscriber = new PatchSubscriber();
$builder->addEventSubscriber($subscriber);
$builder->add('name');
....
}
In this case you're ok to use the PATCH REST requests to edit an entity just by the sent fields

Resources