Symfony2:两个属性都不存在并且具有PropertyAccess的公共访问权限,但是它存在

时间:2015-05-19 13:59:26

标签: php symfony collections accessor

在我的symfony2应用程序中,我正在克隆一个对象及其集合。 我正在使用PropertyAccess类来设置属性值,但我觉得我缺少一个方法访问器来将元素添加到集合中。

编辑:我发现PropertyAccess也可以调用add方法

我的代码:

        $collectionNames = array('tags', 'objectCategories', 'groups');
        foreach ($collectionNames as $collectionName)
        {
            $collection = $accessor->getValue($this, $collectionName);
            $this->$collectionName = new ArrayCollection();
            foreach ($collection as $element)
            {
                $accessor->setValue($this, $collectionName, $element);
            }
        }

我收到以下错误:

Neither the property "objectCategories" nor one of the methods "addObjectCategory()"/"removeObjectCategory()", "setObjectCategories()", "objectCategories()", "__set()" or "__call()" exist and have public access in class "AppBundle\Entity\FoodAnalytics\Recipe".
500 Internal Server Error - NoSuchPropertyException

但是addObjectCategory方法存在并具有公共访问权限,您可以在我的课程中看到

我该如何解决这个问题? 非常感谢!

编辑:整个食谱类:

<?php
//D:\Divers\Programmation\Web\foodmeup\srcRecipe.php

namespace AppBundle\Entity\FoodAnalytics;

use AppBundle\Entity\Core\Media;
use AppBundle\Entity\Core\ObjectCategory;
use AppBundle\Entity\User\User;
use AppBundle\Model\Interfaces\MediaInterface;
use AppBundle\Model\Interfaces\VoteInterface;
use AppBundle\Model\Traits\BlameableTrait;
use AppBundle\Model\Interfaces\ViewCountInterface;
use AppBundle\Model\Traits\MediaTrait;
use AppBundle\Model\Traits\ViewCountTrait;
use AppBundle\Model\Traits\VoteTrait;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping AS ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use AppBundle\Model\Classes\BaseCategoryClass as BaseCategory;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraints as Assert;
use AppBundle\Validator\Constraints as FMUAssert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

/**
 * @ORM\Entity(repositoryClass="AppBundle\Repository\FoodAnalytics\RecipeRepository")
 * @Gedmo\Tree(type="nested")
 * @ORM\HasLifecycleCallbacks
 */
class Recipe extends BaseCategory implements ViewCountInterface, VoteInterface, MediaInterface
{
    use TimestampableEntity, ViewCountTrait, BlameableTrait, VoteTrait, MediaTrait;

    /**
     * @Assert\Type(type="bool", groups={"recipe"})
     * @Assert\NotNull(groups={"recipe"})
     * @ORM\Column(type="boolean", nullable=false)
     */
    protected $isProduct = false;

    /**
     * @Assert\Type(type="integer", groups={"recipe"})
     * @ORM\Column(type="integer", nullable=true)
     */
    protected $portions;

    /**
     * @Assert\Length(min="1", max="100", groups={"recipe"})
     * @Assert\NotBlank(groups={"recipe"})
     * @ORM\Column(type="string", length=100, nullable=false)
     */
    protected $nickname;

    /**
     * @Assert\Length(max="500", groups={"recipe"})
     * @ORM\Column(type="string", length=500, nullable=true)
     */
    protected $shortDescription;

    /**
     * @Assert\Type(type="numeric", groups={"recipe"})
     * @ORM\Column(type="decimal", precision=10, scale=2, nullable=true)
     */
    protected $weight;

    /**
     * @Assert\Type(type="bool", groups={"recipe"})
     * @ORM\Column(type="boolean", nullable=false)
     */
    protected $isPrivate = false;

    /**
     * @Assert\Valid()
     * @ORM\OrderBy({"sortablePosition" = "ASC"})
     * @ORM\OneToMany(targetEntity="RecipeIngredient", mappedBy="recipe", cascade={"persist", "remove"})
     */
    protected $recipeIngredients;

    /**
     * @Assert\Valid()
     * @ORM\OrderBy({"sortablePosition" = "ASC"})
     * @ORM\OneToMany(targetEntity="RecipeAsset", mappedBy="recipe", cascade={"persist", "remove"})
     */
    protected $recipeAssets;

    /**
     * @Assert\Valid()
     * @ORM\OrderBy({"sortablePosition" = "ASC"})
     * @ORM\OneToMany(targetEntity="RecipeSubrecipe", mappedBy="parentRecipe", cascade={"persist"})
     */
    protected $subrecipes;

    /**
     * @ORM\OneToMany(targetEntity="RecipeSubrecipe", mappedBy="subrecipe")
     */
    protected $parentRecipes;

    /**
     * @Assert\Valid()
     * @ORM\OrderBy({"sortablePosition" = "ASC"})
     * @ORM\OneToMany(targetEntity="RecipeStep", mappedBy="recipe", cascade={"persist", "remove"})
     */
    protected $recipeSteps;

    /**
     * @ORM\OrderBy({"sortablePosition" = "ASC"})
     * @Assert\Valid()
     * @ORM\OneToMany(targetEntity="\AppBundle\Entity\Core\Media", mappedBy="recipe", cascade={"persist", "remove"})
     */
    protected $medias;

    /**
     * @ORM\ManyToMany(targetEntity="\AppBundle\Entity\Core\Tag", inversedBy="recipes", cascade={"persist"})
     * @ORM\JoinTable(
     *     name="recipe_tag",
     *     joinColumns={@ORM\JoinColumn(name="recipeId", referencedColumnName="id", nullable=false, onDelete="CASCADE")},
     *     inverseJoinColumns={@ORM\JoinColumn(name="tagId", referencedColumnName="id", nullable=false, onDelete="CASCADE")}
     * )
     */
    protected $tags;

    /**
     * @ORM\OneToMany(targetEntity="UserRecipe", mappedBy="recipe", cascade={"persist", "remove"})
     */
    protected $userRecipes;

    /**
     * @ORM\ManyToMany(targetEntity="AppBundle\Entity\Core\ObjectCategory", mappedBy="recipes", cascade={"persist"})
     */
    protected $objectCategories;

    /**
     * @Gedmo\TreeParent
     * @ORM\ManyToOne(targetEntity="Recipe", inversedBy="children")
     * @ORM\JoinColumn(name="parentId", referencedColumnName="id", onDelete="SET NULL")
     */
    protected $parent;

    /**
     * @ORM\OneToMany(targetEntity="Recipe", mappedBy="parent")
     */
    protected $children;

    /**
     * @ORM\ManyToMany(targetEntity="\AppBundle\Entity\User\Group", inversedBy="recipes")
     * @ORM\JoinTable(
     *     name="recipe_group",
     *     joinColumns={@ORM\JoinColumn(name="recipeId", referencedColumnName="id", onDelete="CASCADE")},
     *     inverseJoinColumns={@ORM\JoinColumn(name="groupId", referencedColumnName="id", onDelete="CASCADE")}
     * )
     */
    protected $groups;

    /**
     * @ORM\ManyToOne(targetEntity="\AppBundle\Entity\User\User")
     * @ORM\JoinColumn(name="userId", referencedColumnName="id", nullable=true)
     */
    protected $user;

    /**
     * @ORM\OneToMany(targetEntity="\AppBundle\Entity\Core\Vote", mappedBy="recipe", cascade={"persist"})
     */
    protected $votes;

    public function isShared()
    {
        return $this->userRecipes->count() > 1;
    }

    public function __toString()
    {
        return $this->name;
    }

    /**
     * http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/implementing-wakeup-or-clone.html
     */
    public function __clone()
    {
        if ($this->id) {
            //Necessary to update slug correctly
            $this->id = null;
            $this->setSlug(null);

            $collectionNames = array('subrecipes', 'recipeIngredients', 'recipeAssets', 'recipeSteps');
            $accessor = PropertyAccess::createPropertyAccessor();

            foreach ($collectionNames as $collectionName)
            {
                $collection = $accessor->getValue($this, $collectionName);
                $this->$collectionName = new ArrayCollection();
                foreach ($collection as $element)
                {
                    $newElement = clone $element;
                    $newElement->setId(null);
                    if (property_exists(get_class($newElement), 'slug')) $newElement->setSlug(null);
                    $method = 'add' . substr(ucfirst($collectionName), 0, -1);
                    $this->$method($newElement);
                }
            }
            //The new recipe is necessarily not yet included in any other recipe
            $this->parentRecipes = new ArrayCollection();
        }
        // otherwise do nothing, do NOT throw an exception!
    }

    //Useful when cloning the recipe and keeping only the user old userrecipe (removing other users' userrecipes)
    public function setUserRecipes($userRecipes)
    {
        $this->userRecipes = $userRecipes;
    }

    /**
     * @Assert\Callback()
     * @param ExecutionContextInterface $context
     */
    public function validate(ExecutionContextInterface $context)
    {
        if (!(count($this->recipeIngredients) + count($this->subrecipes))) {
            $context
                ->buildViolation('Vous devez indiquer au moins un ingrédient ou une sous-recette')
                ->addViolation();
        }
    }

    /**
     * Sets the weight on a recipe by checking for its ingredients and subrecipes weight
     * It's a recursion with subrecipes so subrecipes shall not contain any of the parent recipes !
     *
     * @return $this
     * @ORM\PrePersist()
     */
    public function setWeight()
    {
        /**
         * @var float
         */
        $weight=0;

        /** @var $recipeIngredient RecipeIngredient */
        foreach($this->getRecipeIngredients() as $recipeIngredient)
        {
            if ($recipeIngredient->getUnit()->getName() == 'g')
            {
                $weight += $recipeIngredient->getQuantity();
            }
            elseif ($recipeIngredient->getUnit()->getName() == 'U')
            {
                $weight += $recipeIngredient->getQuantity() * $recipeIngredient->getProduct()->getUnitWeight();
            }
            elseif ($recipeIngredient->getUnit()->getName() == 'produit')
            {
                $weight += $recipeIngredient->getQuantity() * $recipeIngredient->getProduct()->getProductWeight();
            }
        }

        foreach($this->getSubrecipes() as $subrecipe)
        {
            /**
             * @var $subrecipe RecipeSubrecipe
             */
            $weight += $subrecipe->getSubrecipe()->getWeight();
        }

        $this->weight = $weight;

        return $this;
    }

    /**
     * Get weight
     *
     * @return float
     */
    public function getWeight()
    {
        return $this->weight;
    }

    /**
     * Constructor
     * @param User $user
     */
    public function __construct(User $user)
    {
        parent::__construct();
        $this->recipeIngredients = new ArrayCollection();
        $this->recipeAssets = new ArrayCollection();
        $this->subrecipes = new ArrayCollection();
        $this->groups = new ArrayCollection();
        $this->userRecipes = new ArrayCollection();
        $this->recipeSteps = new ArrayCollection();
        $this->medias = new ArrayCollection();
        $this->objectCategories = new ArrayCollection();
        $this->tags = new ArrayCollection();
        $this->parentRecipes = new ArrayCollection();
        $this->children = new ArrayCollection();
        $this->votes = new ArrayCollection();
        $this->user = $user;
    }

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

    /**
     * Set isPrivate
     *
     * @param $isPrivate
     * @return $this
     */
    public function setIsPrivate($isPrivate)
    {
        $this->isPrivate = $isPrivate;

        return $this;
    }

    /**
     * Set portions
     *
     * @param string $portions
     *
     * @return Recipe
     */
    public function setPortions($portions)
    {
        $this->portions = $portions;

        return $this;
    }

    /**
     * Get portions
     *
     * @return float
     */
    public function getPortions()
    {
        return $this->portions;
    }

    /**
     * Set nickname
     *
     * @param string $nickname
     *
     * @return Recipe
     */
    public function setNickname($nickname)
    {
        $this->nickname = $nickname;

        return $this;
    }

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

    /**
     * Set shortDescription
     *
     * @param string $shortDescription
     *
     * @return Recipe
     */
    public function setShortDescription($shortDescription)
    {
        $this->shortDescription = $shortDescription;

        return $this;
    }

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

    /**
     * Set slug
     *
     * @param string $slug
     *
     * @return Recipe
     */
    public function setSlug($slug)
    {
        $this->slug = $slug;

        return $this;
    }

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

    /**
     * Add recipeIngredient
     *
     * @param RecipeIngredient $recipeIngredient
     *
     * @return Recipe
     */
    public function addRecipeIngredient(RecipeIngredient $recipeIngredient)
    {
        $this->recipeIngredients[] = $recipeIngredient;
        //To be added so as to set the recipeId of the recipeIngredient when a new recipe is created
        $recipeIngredient->setRecipe($this);

        return $this;
    }

    /**
     * Remove recipeIngredient
     *
     * @param RecipeIngredient $recipeIngredient
     */
    public function removeRecipeIngredient(RecipeIngredient $recipeIngredient)
    {
        $this->recipeIngredients->removeElement($recipeIngredient);
    }

    /**
     * Get recipeIngredients
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getRecipeIngredients()
    {
        return $this->recipeIngredients;
    }

    /**
     * Add recipeAsset
     *
     * @param RecipeAsset $recipeAsset
     *
     * @return Recipe
     */
    public function addRecipeAsset(RecipeAsset $recipeAsset)
    {
        if (!$this->recipeAssets->contains($recipeAsset))
        {
            $this->recipeAssets[] = $recipeAsset;
            $recipeAsset->setRecipe($this);
        }

        return $this;
    }

    /**
     * Remove recipeAsset
     *
     * @param RecipeAsset $recipeAsset
     */
    public function removeRecipeAsset(RecipeAsset $recipeAsset)
    {
        $this->recipeAssets->removeElement($recipeAsset);
    }

    /**
     * Get recipeAssets
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getRecipeAssets()
    {
        return $this->recipeAssets;
    }

    /**
     * Add subrecipe
     *
     * @param RecipeSubrecipe $subrecipe
     *
     * @return Recipe
     */
    public function addSubrecipe(RecipeSubrecipe $subrecipe)
    {
        $this->subrecipes[] = $subrecipe;
        $subrecipe->setParentRecipe($this);

        return $this;
    }

    /**
     * Remove subrecipe
     *
     * @param RecipeSubrecipe $subrecipe
     */
    public function removeSubrecipe(RecipeSubrecipe $subrecipe)
    {
        $this->subrecipes->removeElement($subrecipe);
    }

    /**
     * Get subrecipes
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getSubrecipes()
    {
        return $this->subrecipes;
    }

    /**
     * Add parentRecipe
     *
     * @param RecipeSubrecipe $parentRecipe
     *
     * @return Recipe
     */
    public function addParentRecipe(RecipeSubrecipe $parentRecipe)
    {
        $this->parentRecipes[] = $parentRecipe;
        $parentRecipe->setParentRecipe($this);

        return $this;
    }

    /**
     * Remove parentRecipe
     *
     * @param RecipeSubrecipe $parentRecipe
     */
    public function removeParentRecipe(RecipeSubrecipe $parentRecipe)
    {
        $this->parentRecipes->removeElement($parentRecipe);
    }

    /**
     * Get parentRecipes
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getParentRecipes()
    {
        return $this->parentRecipes;
    }

    /**
     * Add recipeStep
     *
     * @param RecipeStep $recipeStep
     *
     * @return Recipe
     */
    public function addRecipeStep(RecipeStep $recipeStep)
    {
        $this->recipeSteps[] = $recipeStep;
        $recipeStep->setRecipe($this);

        return $this;
    }

    /**
     * Remove recipeStep
     *
     * @param RecipeStep $recipeStep
     */
    public function removeRecipeStep(RecipeStep $recipeStep)
    {
        $this->recipeSteps->removeElement($recipeStep);
    }

    /**
     * Get recipeSteps
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getRecipeSteps()
    {
        return $this->recipeSteps;
    }

    /**
     * Add media
     *
     * @param \AppBundle\Entity\Core\Media $media
     *
     * @return Recipe
     */
    public function addMedia(\AppBundle\Entity\Core\Media $media)
    {
        $media->setRecipe($this);
        $this->medias[] = $media;

        return $this;
    }

    /**
     * Remove media
     *
     * @param \AppBundle\Entity\Core\Media $media
     */
    public function removeMedia(\AppBundle\Entity\Core\Media $media)
    {
        $this->medias->removeElement($media);
    }

    /**
     * Get medias
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getMedias()
    {
        return $this->medias;
    }

    /**
     * Sets user.
     *
     * @param  string $user
     * @return $this
     */
    public function setUser($user)
    {
        $this->user = $user;

        return $this;
    }

    /**
     * Returns user.
     *
     * @return User
     */
    public function getUser()
    {
        return $this->user;
    }

    /**
     * Add objectCategory
     *
     * @param ObjectCategory $objectCategory
     *
     * @return Recipe
     */
    public function addObjectCategory(ObjectCategory $objectCategory)
    {
        $objectCategory->addRecipe($this);
        $this->objectCategories[] = $objectCategory;

        return $this;
    }

    /**
     * Remove objectCategory
     *
     * @param ObjectCategory $objectCategory
     */
    public function removeObjectCategory(ObjectCategory $objectCategory)
    {
        $this->objectCategories->removeElement($objectCategory);
    }

    /**
     * Get objectCategories
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getObjectCategories()
    {
        return $this->objectCategories;
    }

    /**
     * @return array
     */
    public function getObjectCategoriesWithParents()
    {
        $categories = array();

        /** @var $category ObjectCategory */
        foreach ($this->objectCategories as $category)
        {
            if ($category->getParent()) $categories[] = $category->getParent();
            $categories[] = $category;
        }

        return array_unique($categories);
    }


    /**
     * Set isProduct
     *
     * @param boolean $isProduct
     *
     * @return Recipe
     */
    public function setIsProduct($isProduct)
    {
        $this->isProduct = $isProduct;

        return $this;
    }

    /**
     * Get isProduct
     *
     * @return boolean
     */
    public function getIsProduct()
    {
        return $this->isProduct;
    }

    /**
     * Add userRecipe
     *
     * @param \AppBundle\Entity\FoodAnalytics\UserRecipe $userRecipe
     *
     * @return Recipe
     */
    public function addUserRecipe(\AppBundle\Entity\FoodAnalytics\UserRecipe $userRecipe)
    {
        $this->userRecipes[] = $userRecipe;

        return $this;
    }

    /**
     * Remove userRecipe
     *
     * @param \AppBundle\Entity\FoodAnalytics\UserRecipe $userRecipe
     */
    public function removeUserRecipe(\AppBundle\Entity\FoodAnalytics\UserRecipe $userRecipe)
    {
        $this->userRecipes->removeElement($userRecipe);
    }

    /**
     * Get userRecipes
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getUserRecipes()
    {
        return $this->userRecipes;
    }

    /**
     * Get duration
     *
     * @return \Datetime
     */
    public function getOwnDuration()
    {
        $duration = 0;

        /** @var $recipeStep RecipeStep */
        foreach ($this->recipeSteps as $recipeStep)
        {
            $duration += $recipeStep->getDuration();
        }

        return $duration;
    }

    /**
     * Get duration
     *
     * @return int
     */
    public function getRawTotalDuration()
    {
        $duration = 0;

        /** @var $subrecipe RecipeSubrecipe */
        foreach ($this->subrecipes as $subrecipe)
        {
            $duration += $subrecipe->getSubrecipe()->getRawTotalDuration();
        }

        $duration += $this->getOwnDuration();

        return $duration;
    }

    /**
     * Get duration
     *
     * @param int $factor
     * @return string
     */
    public function getTotalDuration($factor = 1)
    {
        $seconds = $this->getRawTotalDuration() * $factor;

        $hours = floor($seconds/3600);
        $seconds -= $hours * 3600;
        $minutes = floor($seconds/60);
        $seconds -= $minutes *60;

        $string='';
        if ($hours) $string .= $hours . 'h';
        $string = $string ? $string . ' ' : $string;
        if ($minutes) $string.= $minutes .'min';
        $string = $string ? $string . ' ' : $string;
        if ($seconds) $string .= $seconds . 's';

        return trim($string);
    }

    /**
     * Add tags
     *
     * @param \AppBundle\Entity\Core\Tag $tags
     * @return Recipe
     */
    public function addTag(\AppBundle\Entity\Core\Tag $tags)
    {
        $this->tags[] = $tags;

        return $this;
    }

    /**
     * Remove tags
     *
     * @param \AppBundle\Entity\Core\Tag $tags
     */
    public function removeTag(\AppBundle\Entity\Core\Tag $tags)
    {
        $this->tags->removeElement($tags);
    }

    /**
     * Get tags
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getTags()
    {
        return $this->tags;
    }

    /**
     * Add group
     *
     * @param \AppBundle\Entity\User\Group $group
     *
     * @return Recipe
     */
    public function addGroup(\AppBundle\Entity\User\Group $group)
    {
        if (!$this->groups->contains($group))
        {
            $group->addRecipe($this);
            $this->groups[] = $group;
        }

        return $this;
    }

    /**
     * Remove group
     *
     * @param \AppBundle\Entity\User\Group $group
     */
    public function removeGroup(\AppBundle\Entity\User\Group $group)
    {
        $this->groups->removeElement($group);
    }

    /**
     * Get groups
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getGroups()
    {
        return $this->groups;
    }


    /**
     * @return $this
     */
    public function reinitiateGroups()
    {
        $this->groups = new ArrayCollection();

        return $this;
    }

}

2 个答案:

答案 0 :(得分:0)

你可以这样做,:

$var = $acc->getValue($this,$index);
$var->addElement('element');
$acc->setValue($this,$index,$var);

它会正常工作

[编辑]

当您在班级中使用ArrayCollection时,我的代码将与您的实际代码一起使用。

当您使用

获取值时

getValue($this,$index)

,它返回一个已经有addElement方法的ArrayCollection对象,所以这应该可以正常工作。

答案 1 :(得分:0)

好的,错误与我使用访问者的方式有关。 以下编辑解决了该问题:

        foreach ($collectionNames as $collectionName => $clone)
        {
            $collection = $accessor->getValue($this, $collectionName);
            $this->$collectionName = new ArrayCollection();
            $newCollection = new ArrayCollection();
            foreach ($collection as $element)
            {
                $newElement = $clone ? clone $element : $element;
                $newCollection->add($newElement);
            }
            $accessor->setValue($this, $collectionName, $newCollection);
        }