В моем приложении symfony2 я клонирую объект и его коллекции.
Я использую класс PropertyAccess для установки значений свойств, но мне кажется, что мне не хватает метода доступа для добавления элемента в коллекцию.
РЕДАКТИРОВАТЬ: я понял, PropertyAccess также может вызвать метод добавления
Мой код:
$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;
}
}
вы можете сделать это,:
$var = $acc->getValue($this,$index);
$var->addElement('element');
$acc->setValue($this,$index,$var);
это будет работать нормально
[редактировать]Мой код будет работать с вашим реальным кодом, так как вы используете ArrayCollection в своем классе.
когда вы получаете значение с
getValue($this,$index)
, он возвращает вам объект ArrayCollection, у которого уже есть метод addElement, так что это должно работать нормально.
ОК, ошибка была в том, как я использовал аксессор.
Следующее редактирование решает проблему:
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);
}