【问题标题】:Persist data with related entities使用相关实体持久化数据
【发布时间】:2015-12-02 08:16:59
【问题描述】:

我有一个预算表单,它在 select 字段中列出来自客户实体的数据。总结、预算和客户是相互关联的。预算有client_id 列,假设通过从选定的客户端获取id 值来存储它。

<form action="{{ path('panel_add_budget') }}" method="POST">
    <strong>Cliente</strong>
    <div class="form-group">
        <select name="cdg_budget_type[client_id]" class="form-control">
            {% for client in clients %}
                <option value="{{ client.id }}">{{ client.name }}</option>
            {% endfor %}
        </select>
    </div>

    <div class="form-group">
        {{ form_row(form.address) }}
    </div>
    <div class="form-group">
        {{ form_row(form.installments) }}
    </div>
    <div class="form-group">
        {{ form_row(form.check_days) }}
    </div>
    <div class="form-group">
        {{ form_row(form.date_start) }}
    </div>
    <div class="form-group">
        {{ form_row(form.total_value) }}
    </div>

    <button type="submit" class="btn btn-success">Adicionar</button>
</form>

我的客户实体:

class Client
{
    /**
     * @var integer
     */
    private $id;

    /**
     * @var string
     */
    private $name;

    /**
     * @var string
     */
    private $phone;

    /**
     * @var string
     */
    private $email;

    /**
     * @var string
     */
    private $streetName;

    /**
     * @var string
     */
    private $district;

    /**
     * @var string
     */
    private $number;

    /**
     * @var string
     */
    private $city;

    /**
     * @var string
     */
    private $zipCode;

    /**
     * @var integer
     */
    private $budget;


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Client
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set phone
     *
     * @param string $phone
     * @return Client
     */
    public function setPhone($phone)
    {
        $this->phone = $phone;

        return $this;
    }

    /**
     * Get phone
     *
     * @return string 
     */
    public function getPhone()
    {
        return $this->phone;
    }

    /**
     * Set email
     *
     * @param string $email
     * @return Client
     */
    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    /**
     * Get email
     *
     * @return string 
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * Set streetName
     *
     * @param string $streetName
     * @return Client
     */
    public function setStreetName($streetName)
    {
        $this->streetName = $streetName;

        return $this;
    }

    /**
     * Get streetName
     *
     * @return string 
     */
    public function getStreetName()
    {
        return $this->streetName;
    }

    /**
     * Set district
     *
     * @param string $district
     * @return Client
     */
    public function setDistrict($district)
    {
        $this->district = $district;

        return $this;
    }

    /**
     * Get district
     *
     * @return string 
     */
    public function getDistrict()
    {
        return $this->district;
    }

    /**
     * Set number
     *
     * @param string $number
     * @return Client
     */
    public function setNumber($number)
    {
        $this->number = $number;

        return $this;
    }

    /**
     * Get number
     *
     * @return string 
     */
    public function getNumber()
    {
        return $this->number;
    }

    /**
     * Set city
     *
     * @param string $city
     * @return Client
     */
    public function setCity($city)
    {
        $this->city = $city;

        return $this;
    }

    /**
     * Get city
     *
     * @return string 
     */
    public function getCity()
    {
        return $this->city;
    }

    /**
     * Set zipCode
     *
     * @param string $zipCode
     * @return Client
     */
    public function setZipCode($zipCode)
    {
        $this->zipCode = $zipCode;

        return $this;
    }

    /**
     * Get zipCode
     *
     * @return string 
     */
    public function getZipCode()
    {
        return $this->zipCode;
    }

    /**
     * Set budget
     * 
     * @param integer $budget
     */
    function setBudget($budget)
    {
        $this->budget = $budget;
    }

    /**
     * Get budget
     * 
     * @return integer
     */
    function getBudget()
    {
        return $this->budget;
    }
}

预算实体:

class Budget
{
    /**
     * @var integer
     */
    private $id;

    /**
     * @var integer
     */
    private $clientId;

    /**
     * @var string
     */
    private $address;

    /**
     * @var integer
     */
    private $installments;

    /**
     * @var integer
     */
    private $checkDays;

    /**
     * @var \DateTime
     */
    private $dateStart;

    /**
     * @var \DateTime
     */
    private $dateCreated;

    /**
     * @var decimal
     */
    private $totalValue;

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set clientId
     *
     * @param integer $clientId
     * @return Budget
     */
    public function setClientId($clientId)
    {
        $this->clientId = $clientId;

        return $this;
    }

    /**
     * Get clientId
     *
     * @return integer 
     */
    public function getClientId()
    {
        return $this->clientId;
    }

    /**
     * Set address
     *
     * @param string $address
     * @return Budget
     */
    public function setAddress($address)
    {
        $this->address = $address;

        return $this;
    }

    /**
     * Get address
     *
     * @return string 
     */
    public function getAddress()
    {
        return $this->address;
    }

    /**
     * Set installments
     *
     * @param integer $installments
     * @return Budget
     */
    public function setInstallments($installments)
    {
        $this->installments = $installments;

        return $this;
    }

    /**
     * Get installments
     *
     * @return integer 
     */
    public function getInstallments()
    {
        return $this->installments;
    }

    /**
     * Set checkDays
     *
     * @param integer $checkDays
     * @return Budget
     */
    public function setCheckDays($checkDays)
    {
        $this->checkDays = $checkDays;

        return $this;
    }

    /**
     * Get checkDays
     *
     * @return integer 
     */
    public function getCheckDays()
    {
        return $this->checkDays;
    }

    /**
     * Set dateStart
     *
     * @param \DateTime $dateStart
     * @return Budget
     */
    public function setDateStart($dateStart)
    {
        $this->dateStart = $dateStart;

        return $this;
    }

    /**
     * Get dateStart
     *
     * @return \DateTime 
     */
    public function getDateStart()
    {
        return $this->dateStart;
    }

    /**
     * Set dateCreated
     *
     * @param \DateTime $dateCreated
     * @return Budget
     */
    public function setDateCreated($dateCreated)
    {
        $this->dateCreated = $dateCreated;

        return $this;
    }

    /**
     * Get dateCreated
     *
     * @return \DateTime 
     */
    public function getDateCreated()
    {
        return $this->dateCreated;
    }

    /**
     * Set totalValue
     *
     * @param decimal $totalValue
     * @return Budget
     */
    public function setTotalValue($totalValue)
    {
        $this->totalValue = $totalValue;

        return $this;
    }

    /**
     * Get totalValue
     *
     * @return decimal
     */
    public function getTotalValue()
    {
        return $this->totalValue;
    }
}

我的客户与预算的一对多关系:

CDG\PanelBundle\Entity\Client:
    type: entity
    table: client
    repositoryClass: CDG\PanelBundle\Entity\ClientRepository
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    fields:
        name:
            type: string
            length: 255
        phone:
            type: string
            length: 255
        email:
            type: string
            length: 255
        streetName:
            type: string
            length: 255
            column: street_name
        district:
            type: string
            length: 255
        number:
            type: string
            length: 255
        city:
            type: string
            length: 255
        zipCode:
            type: string
            length: 255
            column: zip_code
    oneToMany:
        budget:
            targetEntity: Budget
            mappedBy: clientId
    lifecycleCallbacks: {  }

还有我的预算与客户的多对一关系:

CDG\PanelBundle\Entity\Budget:
    type: entity
    table: budget
    repositoryClass: CDG\PanelBundle\Entity\BudgetRepository
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    fields:
        address:
            type: string
            length: 255
        installments:
            type: integer
        checkDays:
            type: integer
            column: check_days
        dateStart:
            type: datetime
            column: date_start
        dateCreated:
            type: datetime
            column: date_created
        totalValue:
            type: decimal
            column: total_value
            nullable: true
    manyToOne:
        clientId:
            targetEntity: Client
            inversedBy: budget
            joinColumn:
                name: client_id
                referencedColumnName: id
    lifecycleCallbacks: {  }

最后,我的addAction,来自BudgetController

public function addAction(Request $request)
{
    $form = $this->createForm(new BudgetType());

    if ($request->getMethod() == 'POST') {
        $form->handleRequest($request);

        if ($form->isValid()) {
            $manager = $this->getDoctrine()->getManager();
            $manager->persist($form->getData());
            $manager->flush();

            $this->addFlash('success', 'Novo orçamento adicionado');

            return $this->redirect($this->generateUrl('panel_budgets'));
        }
    }

    return $this->render('PanelBundle:Budget:add.html.twig', array(
        'clients' => $manager->getRepository('PanelBundle:Client')->findAll(),
        'form' => $form->createView()
    ));
}

提交表单时,抛出异常:

警告:spl_object_hash() 期望参数 1 是对象,给定整数

public function getEntityState($entity, $assume = null)
{
    $oid = spl_object_hash($entity);
    if (isset($this->entityStates[$oid])) {
        return $this->entityStates[$oid];

它的堆栈跟踪:

at ErrorHandler ->handleError ('2', 'spl_object_hash() expects    parameter 1 to be object, integer given', '/home/gabriel/Documents/CasaDoGesso/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php', '1389',   array('entity' => '2', 'assume' => '2'))
at spl_object_hash ('2')
in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 1389  +
at UnitOfWork ->getEntityState ('2', '2')
in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 788  +
at UnitOfWork ->computeAssociationChanges (array('fieldName' =>    'clientId', 'targetEntity' => 'CDG\PanelBundle\Entity\Client', 'inversedBy' => 'budget', 'joinColumns' => array(array('referencedColumnName' => 'id', 'name' => 'client_id')), 'type' => '2', 'mappedBy' => null, 'isOwningSide' => true, 'sourceEntity' => 'CDG\PanelBundle\Entity\Budget', 'fetch' => '2', 'cascade' => array(), 'isCascadeRemove' => false, 'isCascadePersist' => false, 'isCascadeRefresh' => false, 'isCascadeMerge' => false, 'isCascadeDetach' => false, 'sourceToTargetKeyColumns' => array('client_id' => 'id'), 'joinColumnFieldNames' => array('client_id' => 'client_id'), 'targetToSourceKeyColumns' => array('id' => 'client_id'), 'orphanRemoval' => false), '2')
in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 687  +
at UnitOfWork ->computeChangeSet (object(ClassMetadata), object(Budget))
in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 404  +
at UnitOfWork ->computeScheduleInsertsChangeSets ()
in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 711  +
at UnitOfWork ->computeChangeSets ()
in vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 297  +
at UnitOfWork ->commit (null)
in vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php at line 340  +
at EntityManager ->flush ()
in src/CDG/PanelBundle/Controller/BudgetController.php at line 36  +
at BudgetController ->addAction (object(Request))
at call_user_func_array (array(object(BudgetController), 'addAction'),   array(object(Request)))
in app/bootstrap.php.cache at line 3109  +
at HttpKernel ->handleRaw (object(Request), '1')
in app/bootstrap.php.cache at line 3071  +
at HttpKernel ->handle (object(Request), '1', true)
in app/bootstrap.php.cache at line 3222  +
at ContainerAwareHttpKernel ->handle (object(Request), '1', true)
in app/bootstrap.php.cache at line 2444  +
at Kernel ->handle (object(Request))
in web/app_dev.php at line 28  +

这对我来说没有任何意义。我不知道该怎么办。其他的解决方案根本没有帮助我。如果您需要更多代码,请询问我,我将对其进行编辑。谢谢。

【问题讨论】:

    标签: php symfony doctrine-orm entity-relationship


    【解决方案1】:

    我建议您在实体中使用注释并开始使用表单构建器。还要向客户端实体添加一个 __toString() 方法!

    您的实体:

    namespace AppBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Validator\Constraints as Assert;
    use Doctrine\Common\Collections\ArrayCollection;
    
    /**
     * Client
     *
     * @ORM\Table()
     * @ORM\Entity
     */
    class Client
    {
        /**
         * @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=64)
         */
        private $name;
    
        // YOUR OTHER CLIENT FIELDS
    
        /**
         * @ORM\OneToMany(targetEntity="Budget", mappedBy="client")
         **/
        private $budgets;
    
        public function __construct() {
            $this->budgets = new ArrayCollection();
        }
    
        /* this function is required to present the entity as a string */
        public function __toString() {
             return $this->name;
        }
    
    }
    

    第二个:

    namespace AppBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\Validator\Constraints as Assert;
    
    /**
     * Budget
     *
     * @ORM\Table()
     * @ORM\Entity
     */
    class Budget
    {
        /**
         * @var integer
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
    
        // YOUR BUDGET FIELDS
    
        /**
         * @ORM\ManyToOne(targetEntity="Client", inversedBy="budgets")
         * @ORM\JoinColumn(name="client_id", referencedColumnName="id")
         **/
        private $client;
    
    }
    

    然后从您的项目主目录中将以下命令放入控制台: (也许您必须在每个命令之前输入单词 php

    app/console doctrine:generate:entities AppBundle
    app/console doctrine:schema:update --force
    app/console doctrine:generate:crud AppBundle:Client
    ( type YES by the question Do you want to generate the "write" actions [no]?)
    app/console doctrine:generate:crud AppBundle:Budget
    ( type YES by the question Do you want to generate the "write" actions [no]?)
    

    看看新的路线:

    app/console router:debug
    

    如您所见,您可以浏览到

    web/app_dev.php/client/

    web/app_dev.php/budget/ 看看吧!

    然后在你的编辑器中打开../src/AppBundle/Form/BudgetType.php,你会发现这条规则:

        $builder
            ->add('client')
        ;
    

    这是将客户端属性添加到表单的位置。 Symfony 自动将此表单域设置为entity formtype。你也可以这样写:

    $builder
        ->add('client', 'entity', array(
            'class' => 'AppBundle:Client',
            'empty_value' => '',
        ))
    ;
    

    所以现在您可以在预算表中选择您的客户了。

    您可能已经注意到命令还为您添加了一个 ClientController 和一个 BudgetController。 有关formtypes 的更多信息,请参阅文档。

    【讨论】:

    • 只需在我的客户端实体中的BudgetType__toString() 方法中添加entity 字段类型即可解决此问题。我不需要做任何其他事情,比如新路线和新 Bundle,这会破坏我的项目结构。对于您的建议:我宁愿使用 YAML 而不是注释,因为对我来说代码看起来更有条理,即使我必须再写一些。无论如何,谢谢你。
    • 很高兴听到你能解决它。还是不得不说应该用formbuilder。它最好是生成表单。
    • 你说的这个表单生成器是什么?是自动生成还是别的? Doctrine 生成表单和内容的方式确实丑陋且混乱。我认为在生成所有内容之前了解正在发生的事情很重要。面对这样的问题很好,有人愿意为你写出巨大的解决方案更好。再次感谢你。 :)
    • 如果您对可能性有共同点,这个表单构建器会为您生成漂亮的表单。如果您说您必须了解正在发生的事情,我同意,但 Symfony 的表单构建器(不是 Doctrine)确实是“必须学习”的。从这里开始:symfony.com/doc/current/book/forms.html
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 2015-07-27
    • 2020-06-15
    相关资源
    最近更新 更多