带有ManyToOne的formtype导致其他字段错误(null)

时间:2015-01-10 10:49:22

标签: php symfony doctrine-orm symfony-2.6

我试图创建以下结构,但得到1个错误让我发疯。

客户有很多发票,发票属于客户

我使用symfony2 formtype,doctrine orm实体,

CRUD工作正常,没有发票实体中的OneToMany,但在添加OneToMany关系后,则创建和更新始终抛出空错误

db结构:

client - id, username, email, [.....]

invoice - id, client_id, total, [.....]

发票表client_id字段具有对客户表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' => 'admin@admin.com', '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)会突然变为空,我使用浏览器检查inspect元素隐藏的输入是否仍在那里

因为我看到发票实体有

/**
* @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;

这两个人都瞄准同一个字段client_id,我觉得这是错误的原因,但我不知道为什么,也不知道如何解决,任何想法的人?很多人欣赏

1 个答案:

答案 0 :(得分:0)

我找到了一个解决方案,在持久化之前使用setClient和handleRequest来设置客户端,然后问题就是修复,所以我也从Invoice实体中删除了clientId,没有必要复制

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()
    ));
}