在非对象上调用成员函数 removeElement()

Posted

技术标签:

【中文标题】在非对象上调用成员函数 removeElement()【英文标题】:Call to a member function removeElement() on a non-object 【发布时间】:2015-12-23 02:39:13 【问题描述】:

我正在创建一个表单,以便能够向链接到特定课程的一组学生发送电子邮件。默认情况下,必须选择给定课程的所有学生,但消息的发送者必须能够取消选择学生以将他们排除在接收消息之外。发送到整个组是没有问题的。从选择中删除学生时会出现问题。

我将 Sonata Admin 的 sonata_type_model 与自定义查询一起使用。在生成的表单上,如果我不更改选择选项并提交表单,一切正常。当我从列表中删除一个项目时,我在提交表单后收到错误:

错误:在 /xxx/xxx/xxx/vendor/sonata-project/doctrine-orm-admin-bundle/Model/ModelManager.php 第 607 行中的非对象上调用成员函数 removeElement()

经过两天寻找答案,希望这里有人可以帮助我朝着正确的方向前进。这是我使用的一些代码:

管理员:

$em = $this->modelManager->getEntityManager('Stnu\EduBundle\Entity\DealItem');
    $query = $em->createQueryBuilder('d')
            ->select('d')
            ->from('StnuEduBundle:DealItem', 'd')
            ->innerJoin('d.deal', 'de')
            ->where('d.course = :course')
            ->andWhere('de.status = :status')
            ->setParameter('course',$course)
            ->setParameter('status','order');

    $defaults = $query->getQuery()->getResult();


    $formMapper
            ->with('Certificaten verzenden cursus \''. $title .'\'', array('description' => 'Begeleidende tekst e-mail'))
                ->add('dealItems', 'sonata_type_model', array(
                    'required' => true,
                    'expanded' => false,
                    'btn_add' => false,
                    'multiple' => true,
                    'label' => 'Verzenden aan',
                    'query' => $query,
                    'property' => 'deal.user',
                    'data' => $defaults,
                    'validation_groups' => false
                ))
                ->add('subject', 'text', array('required' => true, 'label' => 'Onderwerp', 'data' => $subject))
                ->add('body', 'textarea', array('label' => 'Bericht', 'required' => false, 'data' => $body, 'attr' => array('class' => 'tinymce', 'data-theme' => 'fullpage', 'style' => 'height: 350px')));

控制器:

/**
 * Create action
 *
 * @return Response
 *
 * @throws AccessDeniedException If access is not granted
 */
public function createAction()


    // the key used to lookup the template
    $templateKey = 'edit';

    if (false === $this->admin->isGranted('CREATE')) 
        throw new AccessDeniedException();
    

    $object = $this->admin->getNewInstance();

    $this->admin->setSubject($object);


    /** @var $form \Symfony\Component\Form\Form */
    $form = $this->admin->getForm();
    $form->setData($object);


    if ($this->getRestMethod()== 'POST') 

        $object->setDealItems($object->getDealItems());

        $form->submit($this->get('request'));

错误出现在这一点之后。

实体:

<?php

namespace Stnu\EduBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * DocsEmail
 * 
 * @ORM\Entity
 */
class CertificateEmail 

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;


    /**
     * @ORM\ManyToMany(targetEntity="DealItem")
     * @ORM\JoinTable(name="certificateemails_dealitems",
     *      joinColumns=@ORM\JoinColumn(name="certificateEmail_id", referencedColumnName="id"),
     *      inverseJoinColumns=@ORM\JoinColumn(name="dealItem_id", referencedColumnName="id")
     *      )
     */
    private $dealItems;

    private $subject;

    private $body;

    private $extraEmailTo;

    public function __construct() 
        $this->dealItems = new ArrayCollection();
    

    /**
     * Add dealItem
     *
     */
    public function addDealItem(\Stnu\EduBundle\Entity\DealItem $dealItem) 

        $this->dealItems->add($dealItem);
        //$this->dealItems[] = $dealItem;
        return $this;
    

    /**
     * Remove dealItem
     */
    public function removeDealItem(\Stnu\EduBundle\Entity\DealItem $dealItem) 

        foreach ($this->dealItems as $item) 
            if ($dealItem === $item) 
                // manager of Stnu\EduBundle\Entity\DealItem
                $entityManager->remove($dealItem);
            
        

    

    /**
     * Get dealItems
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getDealItems() 

        return $this->dealItems;
    

    public function setDealItems($dealItems) 
        $this->dealItems = new ArrayCollection();

        if (count($dealItems) > 0) 
            foreach ($dealItems as $dealItem) 
                $this->addDealItem($dealItem);
            
        

        return $this;
    


    /**
     * Set subject
     *
     * @param string $subject
     */
    public function setSubject($subject) 
        $this->subject = $subject;

        return $this;
    

    /**
     * Get subject
     *
     * @return string 
     */
    public function getSubject() 
        return $this->subject;
    

    /**
     * Set body
     *
     * @param string $body
     */
    public function setBody($body) 
        $this->body = $body;

        return $this;
    

    /**
     * Get body
     *
     * @return string 
     */
    public function getBody() 
        return $this->body;
    

    /**
     * Set extraEmailTo
     *
     * @param string $extraEmailTo
     */
    public function setExtraEmailTo($extraEmailTo) 
        $this->extraEmailTo = $extraEmailTo;

        return $this;
    

    /**
     * Get extraEmailTo
     *
     * @return string 
     */
    public function getExtraEmailTo() 
        return $this->extraEmailTo;
    


希望有人可以帮助我!

【问题讨论】:

错误提示 dealItems 在调用 removeElement() 时为空。我可以看到您在构造对象时正在初始化集合 - 所以在我看来,这意味着沿线某处, dealItems 被破坏了......但我看不到在哪里 这是怎么回事 - $object-&gt;setDealItems($object-&gt;getDealItems()); ? 我想只是一个测试,看看它是否有帮助。我将删除此代码。 亲爱的 Ragdata 和 Stijlnu!感谢我的声誉;) 这个也很可疑@Stijlnu - $form-&gt;submit($this-&gt;get('request')); 您在上面的评论中引用的 Form 类(我做同样的事情来帮助我的 IDE)没有 submit 方法...你扩展了吗? 【参考方案1】:

我相信这越来越接近您的问题的答案。问题是,你问了一个完全错误的问题。重要的东西在下面,在“编辑 - 自定义多选字段”下。

你的removeDealItem 方法全错了。试试这个:

    public function removeDealItem(\Stnu\EduBundle\Entity\DealItem $dealItem)
    
        $this->dealItems->removeElement( $dealItem );

        return $this;
    

您没有$entityManager 可以在这里打电话...而且您也不需要。 Doctrine 将检查您要删除的实体是否存在,如果存在则将其删除。您不需要遍历集合中的现有元素,当然也不需要在数据库级别上做任何事情。

    public function addDealItem(\Stnu\EduBundle\Entity\DealItem $dealItem)
    
        // Getting fancy - check if the item exists before adding it
        if( !$this->dealItems->contains($dealItem) )
        
            $this->dealItems->add($dealItem);
        
        return $this;
    

添加一个元素同样简单......我们甚至可以使用它并使用 Doctrine ArrayCollection::contains() 方法在添加之前检查元素是否存在。您的 addDealItem() 方法没有任何问题 - 我只是想向您展示 contains() 作为让 ArrayCollection 类为您完成工作的更明显的说明。

编辑 - 自定义多选字段

好的 - 在阅读了您关于不一定需要保留数据的评论后,我想我会提供这个简化的示例来说明如何创建自定义多选框。请理解这是一个“基本”的例子——但它应该引导你朝着正确的方向前进。显然,在不知道您的 DealItem 实体的结构的情况下,我只是猜测您需要访问的特定字段,以便获取发送电子邮件所需的数据。

所以 - 在您的控制器中 - 首先我们获取数据:

$query = $em->createQueryBuilder('d')
            ->select('d')
            ->from('StnuEduBundle:DealItem', 'd')
            ->innerJoin('d.deal', 'de')
            ->where('d.course = :course')
            ->andWhere('de.status = :status')
            ->setParameter('course',$course)
            ->setParameter('status','order');

$defaults = $query->getQuery()->getResult();

$choices = array();

foreach( $defaults as $dealItem )

    $choices[ $dealItem->getEmailAddress() ] = $dealItem->getStudentName();

现在我们需要一个对象来接收数据。我从您的 cmets 收集到的是您不想保留数据,并且您只为您的 CertificateEmail 对象创建了一个实体,以便您可以构建一个表单。馊主意。 您不需要实体 - 所以一开始就不要构建实体。 为了证明这一点,我将使用 stdClass 对象:

$certificateEmail = new \stdClass();

$certificateEmail->dealItems = array();
$certificateEmail->subject   = '';
$certificateEmail->body      = '';

然后我们构建表单:

$form = $this->createFormBuilder( $certificateEmail )
             ->add( 'dealItems', 'choice', array(
                         'choices'  => $choices,
                         'multiple' => true,
                         'required' => true,
                         'label'    => 'Verzenden aan' ) )
             ->add( 'subject', 'text', array( 'required' => true, 'label' => 'Onderwerp' ) )
             ->add( 'body', 'textarea', array( 'required' => false, 'label' => 'Bericht' ) )
             ->getForm();

最后,把它扔到一个模板上:

return $this->render( 'template.html.twig', array( 'form' => $form->createView() ) );

而且,希望你能从那里拿走它:)

【讨论】:

removeDealItem 确实需要修复 - 但我开始认为这不是你的问题所在.. 作为测试,我刚刚从头开始创建了一个新的实体和表单管理类,没有大部分自定义编码。我只是保留了 sonata_type_collection 的自定义查询。这次我让它持久化到数据库中。出现同样的问题。仅当我不从选择中删除项目时,持久化才能正常工作。没有自定义查询的测试将无法正常工作,因为我收到了 OutOfMemoryException。 @Stijlnu - 试一试更新后的答案,让我知道你的进展 嗯...我刚刚发现如果我从表单映射器中删除数据属性(希望我使用的是正确的术语),一切正常。 //'data' =&gt; $defaults, 我终于能够通过将$defaults = $query-&gt;getQuery()-&gt;getResult();更改为$defaults = new Collections\ArrayCollection($query-&gt;getQuery()-&gt;getResult());来解决它【参考方案2】:

在删除操作之前检查您的收藏。

ldd($object->getDealItems()); // or var_dump();die();

我认为当调用删除操作时,您的属性 dealItems 是空的。

【讨论】:

removeElement() 是 Doctrine 的 ArrayCollection 类的一种方法......我不认为这是这里的问题 需要更多代码......现在没有多大意义。不知何故,某处某事设法取消设置您的 DealItem 实体的 private 属性......这必须发生在实体本身的某处。 我仍然想知道$object-&gt;setDealItems($object-&gt;getDealItems()); 行的目的是什么。根据您发布的代码,它应该 真的 不重要,因为它看起来好像会导致一个空的 ArrayCollection 对象......但它看起来对我来说有点可疑。 这对我来说真的没有意义——它在你的实体中设置了两次——一次是在对象初始化时,一次是在你执行 setDealItems() 方法时。您的 Entity 类中的某些内容必须取消设置...它是私有属性。 我用实体的完整代码更新了我的问题

以上是关于在非对象上调用成员函数 removeElement()的主要内容,如果未能解决你的问题,请参考以下文章

在非对象上调用成员函数 getClientOriginalName()

在非构造的“对象”上调用非虚拟成员函数是不是定义明确? [复制]

致命错误:在非对象上调用成员函数 fetch()?

致命错误:在非对象上调用成员函数 getLoginUrl()

FatalErrorException:错误:在非对象 symfony 上调用成员函数 has()

致命错误:在非对象 cakephp 上调用成员函数 save()