Я пытаюсь создать следующую структуру, но получаю 1 ошибку, которая сводит меня с ума.
клиент имеет много счетов, счет принадлежит клиенту
я использую symfony2 formtype, сущность доктрины,
CRUD работает нормально без OneToMany в объекте накладной, но после добавления отношения OneToMany, Create и Update всегда выдают нулевую ошибку
структура БД:
client - id, username, email, [.....]
invoice - id, client_id, total, [.....]
поле client_id таблицы счетов имеет ссылку внешнего ключа на поле идентификатора таблицы клиента
контроллер:
public function createAction($id, Request $request) {
$form = $this->createCreateForm(new Invoice, $id);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->get('doctrine')->getManager();
$em->persist($form->getData());
$em->flush();
$this->addFlash('success', 'Invoice is created.');
return $this->redirectToRoute('client_invoice_list', array('id' => $id));
} else {
$errors = (string)$form->getErrors(true);
$form->addError(new FormError($errors));
}
return $this->render('WebulousEcocrmBundle:Invoice:create.html.twig', array(
'_action' => 'clients',
'form' => $form->createView()
));
}
public function createFormAction($id, Request $request) {
$client = $this->findClientById($id);
if (null == $client) {
return $this->handleNotFound();
}
$invoice = new Invoice();
$form = $this->createCreateForm($invoice, $id);
return $this->render('WebulousEcocrmBundle:Invoice:create.html.twig', array(
'_action' => 'clients',
'form' => $form->createView()
));
}
private function createCreateForm(Invoice $invoice, $id) {
$form = $this->createForm(new InvoiceType, $invoice, array(
'method' => 'POST',
'action' => $this->generateUrl('client_invoice_create_form', array('id' => $id)),
));
// here is the hidden input to store the client_id
// work fine without relationship, but after added relationship it like ignore this
$form->add('clientId', 'hidden', array('data' => $id));
$form->add('stat', 'hidden', array('data' => 0));
$form->add('submit', 'submit');
return $form;
}
здесь субъект счета
<?php
namespace Webulous\EcocrmBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Invoice
*
* @ORM\Table()
* @ORM\Entity(repositoryClass="Webulous\EcocrmBundle\Entity\InvoiceRepository")
*/
class Invoice
{
const STATUS_UNPAID = 0;
const STATUS_PAID = 1;
const STATUS_CANCELLED = 2;
const TAX_CHARGE_PERCENTAGE = 1;
const TAX_CHARGE_FIXED = 2;
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="Client", inversedBy="invoice")
* @ORM\JoinColumn(name="client_id", referencedColumnName="id")
*/
protected $client;
/**
* @var integer
*
* @ORM\Column(name="client_id", type="integer")
*/
private $clientId;
/**
* @var integer
*
* @ORM\Column(name="stat", type="smallint")
*/
private $stat;
[.......]
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
public function getInvoiceId() {
return 'MPN#' . str_pad($this->id, 6, '0', STR_PAD_LEFT);
}
/**
* Set clientId
*
* @param integer $clientId
* @return Invoice
*/
public function setClientId($clientId)
{
$this->clientId = $clientId;
return $this;
}
/**
* Get clientId
*
* @return integer
*/
public function getClientId()
{
return $this->clientId;
}
/**
* Set stat
*
* @param integer $stat
* @return Invoice
*/
public function setStat($stat)
{
$this->stat = $stat;
return $this;
}
/**
* Get stat
*
* @return integer
*/
public function getStat()
{
return $this->stat;
}
[........]
public function getStatExplain() {
return $this->transformStat($this->getStat());
}
private function transformStat($value) {
switch ($value) {
case self::STATUS_UNPAID:
return 'Unpaid';
case self::STATUS_PAID:
return 'Paid';
case self::STATUS_CANCELLED:
return 'Cancelled';
default:
return 'Unpaid';
}
}
private function reverseTransformStat($value) {
switch ($value) {
case 'Unpaid':
return self::STATUS_UNPAID;
case 'Paid':
return self::STATUS_PAID;
case 'Cancelled':
return self::STATUS_CANCELLED;
default:
return self::STATUS_UNPAID;
}
}
/** @ORM\PrePersist */
public function prePresist() {
$this->created = time();
$this->updated = time();
}
/** @ORM\PreUpdate */
public function preUpdate() {
$this->updated = time();
}
/**
* Set client
*
* @param \Webulous\EcocrmBundle\Entity\Client $client
* @return Invoice
*/
public function setClient(\Webulous\EcocrmBundle\Entity\Client $client = null)
{
$this->client = $client;
return $this;
}
/**
* Get client
*
* @return \Webulous\EcocrmBundle\Entity\Client
*/
public function getClient()
{
return $this->client;
}
/**
* Set total
*
* @param integer $total
* @return Invoice
*/
public function setTotal($total) {
$this->total = $total;
return $this;
}
/**
* Get stat
*
* @return integer
*/
public function getTotal() {
//TODO: sum onetomany
return '1000';
// return $this->total;
}
}
а вот клиентская сущность
<?php
namespace Webulous\EcocrmBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Entity
* @ORM\Table(name="client")
* @ORM\HasLifecycleCallbacks()
* @UniqueEntity(fields="username", message="Username is not available.")
*/
class Client
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\OneToMany(targetEntity="Invoice", mappedBy="client")
*/
protected $invoice;
/**
* @ORM\Column(type="string", length=60, unique=true)
*/
protected $username;
[.......]
/**
* @ORM\ManyToMany(targetEntity="Module")
* @ORM\JoinTable(name="clients_modules",
* joinColumns={@ORM\JoinColumn(name="client_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="module_id", referencedColumnName="id")}
* )
**/
protected $modules;
/**
* @ORM\ManyToMany(targetEntity="Transaction")
* @ORM\JoinTable(name="clients_transactions",
* joinColumns={@ORM\JoinColumn(name="client_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="transaction_id", referencedColumnName="id")}
* )
**/
protected $transactions;
/**
* @ORM\ManyToOne(targetEntity="Package", inversedBy="clients")
* @ORM\JoinColumn(name="package_id", referencedColumnName="id", nullable=true)
*/
protected $package;
/**
* Constructor
*/
public function __construct()
{
$this->modules = new ArrayCollection();
$this->invoice = new ArrayCollection();
}
/**
* @ORM\PrePersist
*/
public function prePersist()
{
$this->created = time();
}
/**
* @ORM\PreUpdate
*/
public function preUpdate()
{
$this->updated = time();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
[......]
/**
* Add modules
*
* @param \Webulous\EcocrmBundle\Entity\Module $modules
* @return Client
*/
public function addModule(\Webulous\EcocrmBundle\Entity\Module $modules)
{
$this->modules[] = $modules;
return $this;
}
/**
* Remove modules
*
* @param \Webulous\EcocrmBundle\Entity\Module $modules
*/
public function removeModule(\Webulous\EcocrmBundle\Entity\Module $modules)
{
$this->modules->removeElement($modules);
}
/**
* Get modules
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getModules()
{
return $this->modules;
}
/**
* Add transactions
*
* @param \Webulous\EcocrmBundle\Entity\Transaction $transactions
* @return Client
*/
public function addTransaction(\Webulous\EcocrmBundle\Entity\Transaction $transactions)
{
$this->transactions[] = $transactions;
return $this;
}
/**
* Remove transactions
*
* @param \Webulous\EcocrmBundle\Entity\Transaction $transactions
*/
public function removeTransaction(\Webulous\EcocrmBundle\Entity\Transaction $transactions)
{
$this->transactions->removeElement($transactions);
}
/**
* Get transactions
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getTransactions()
{
return $this->transactions;
}
/**
* Set active
*
* @param boolean $active
* @return Client
*/
public function setActive($active)
{
$this->active = $active;
return $this;
}
/**
* Get active
*
* @return boolean
*/
public function isActive()
{
return $this->active;
}
public function setAaFormNo($val)
{
$this->aaFormNo = $val;
return $this;
}
public function getAaFormNo()
{
return $this->aaFormNo;
}
public function setExpireDate($val)
{
$this->expireDate = $val;
return $this;
}
public function getExpireDate()
{
return $this->expireDate;
}
public function setPackage(Package $package = null)
{
$this->package = $package;
return $this;
}
public function getPackage()
{
return $this->package;
}
/**
* Get active
*
* @return boolean
*/
public function getActive()
{
return $this->active;
}
/**
* Add invoice
*
* @param \Webulous\EcocrmBundle\Entity\Invoice $invoice
* @return Client
*/
public function addInvoice(\Webulous\EcocrmBundle\Entity\Invoice $invoice)
{
$this->invoice[] = $invoice;
return $this;
}
/**
* Remove invoice
*
* @param \Webulous\EcocrmBundle\Entity\Invoice $invoice
*/
public function removeInvoice(\Webulous\EcocrmBundle\Entity\Invoice $invoice)
{
$this->invoice->removeElement($invoice);
}
/**
* Get invoice
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getInvoice()
{
return $this->invoice;
}
}
а вот тип формы
<?php
namespace Webulous\EcocrmBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class InvoiceType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fromCompanyName', 'text', array('label' => 'From Company Name', 'data' => 'Default Company Name', 'required' => false))
->add('toCompanyName', 'textarea', array('label' => 'To Company Name', 'required' => false))
->add('fromCompanyAddress', 'textarea', array(
'label' => 'From Company Address',
'data' => 'From Company Address',
'required' => false
)
)
->add('toCompanyAddress', 'textarea', array('label' => 'To Company Address', 'required' => false))
->add('fromContact', 'text', array('label' => 'From Contact Number', 'data' => '6011111111', 'required' => false))
->add('fromEmail', 'email', array('label' => 'From E-Mail', 'data' => '[email protected]', 'required' => false))
->add('taxType', 'choice', array('label' => 'Tax Type', 'choices' => array(
'1' => 'Charge by percentage',
'2' => 'Charge a fixed cost'
), 'required' => false))
->add('tax', 'text', array('label' => 'Tax', 'required' => false))
->add('taxDescription', 'text', array('label' => 'Tax Description', 'required' => false))
;
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Webulous\EcocrmBundle\Entity\Invoice'
));
}
/**
* @return string
*/
public function getName()
{
return 'webulous_ecocrmbundle_invoice';
}
}
и вот сообщение об ошибке
An exception occurred while executing 'INSERT INTO Invoice (client_id, stat, created, updated, from_company_name, to_company_name, from_company_address, to_company_address, from_contact, from_email, tax_type, tax, tax_description) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' with params [null, [.........]]
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'client_id' cannot be null
я не знаю, почему client_id ($ clientId) внезапно станет нулевым, я использую браузер, чтобы проверить элемент проверки, скрытый ввод все еще там
как я вижу сущность счета
/**
* @ORM\ManyToOne(targetEntity="Client", inversedBy="invoice")
* @ORM\JoinColumn(name="client_id", referencedColumnName="id")
*/
protected $client;
/**
* @var integer
*
* @ORM\Column(name="client_id", type="integer")
*/
private $clientId;
оба 2 нацелены на одно и то же поле client_id, я чувствую, что это является причиной ошибки, но я не знаю почему и не знаю, как это исправить, ребята, идея? многие ценят
Я нашел решение, используйте setClient перед сохранением и handleRequest для установки клиента, затем проблема устранена, поэтому я также удаляю clientId из объекта Invoice, нет необходимости дублировать
public function createAction($id, Request $request) {
$client = $this->findClientById($id);
if (null == $client) {
return $this->handleNotFound();
}
$invoice = new Invoice();
$invoice->setClient($client);
$invoice->setStat(0);
$form = $this->createForm(new InvoiceType, $invoice, array(
'method' => 'POST',
'action' => $this->generateUrl('client_invoice_create_form', array('id' => $id)),
));
$form->add('submit', 'submit');
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->get('doctrine')->getManager();
$em->persist($form->getData());
$em->flush();
$this->addFlash('success', 'Invoice is created.');
return $this->redirectToRoute('client_invoice_list', array('id' => $id));
} else {
$errors = (string)$form->getErrors(true);
$form->addError(new FormError($errors));
}
return $this->render('WebulousEcocrmBundle:Invoice:create.html.twig', array(
'_action' => 'clients',
'form' => $form->createView()
));
}
Других решений пока нет …