我需要一些帮助,我一个月来就面临这个问题并尝试解决它(已经在很多论坛上搜索过,但没有任何效果)。 这里是
PropertyAccessor requires a graph of objects or arrays to operate on, but it found type "NULL" while trying to traverse path "animal.nom" at property "nom".
我使用 Vich Uploader 和目录命名器,它似乎没有获取我的实体的值来获取路径。 仅当我删除实体的图像时才会出现该错误,但上传时没有问题。
这是我的EntityImage的实体文件(名为:AnimalImage)
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\AnimalImageRepository;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
#[ORM\Entity(repositoryClass: AnimalImageRepository::class)]
#[Vich\Uploadable]
class AnimalImage
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'animalImages', cascade: ['persist'])]
#[ORM\JoinColumn(nullable: false)]
private ?Animal $animal = null;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*/
#[Vich\UploadableField(mapping: 'animal', fileNameProperty: 'imageName', size: 'imageSize')]
private ?File $imageFile = null;
#[ORM\Column(length: 500)]
private ?string $imageName = null;
#[ORM\Column(length: 500)]
private ?string $imageSize = null;
#[ORM\Column]
private ?\DateTimeImmutable $updatedAt = null;
public function getId(): ?int
{
return $this->id;
}
public function getAnimal(): ?Animal
{
return $this->animal;
}
public function setAnimal(?Animal $animal): self
{
$this->animal = $animal;
return $this;
}
public function getImageName(): ?string
{
return $this->imageName;
}
public function setImageName(string $imageName): self
{
$this->imageName = $imageName;
return $this;
}
public function getImageSize(): ?int
{
return $this->imageSize;
}
public function setImageSize(int $imageSize): self
{
$this->imageSize = $imageSize;
return $this;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeImmutable $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get the value of imageFile
*/
public function getImageFile(): ?File
{
return $this->imageFile;
}
/**
* Set the value of imageFile
*
* @return self
*/
public function setImageFile(?File $imageFile = null): void
{
$this->imageFile = $imageFile;
if(null !== $imageFile){
$this->updatedAt = new \DateTimeImmutable();
}
}
}
这是我的动物实体的表单类型
namespace App\Form;
use App\Entity\Race;
use App\Entity\Tags;
use App\Entity\Animal;
use App\Entity\Espece;
use App\Form\AnimalImageType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\AbstractType;
use Doctrine\ORM\EntityRepository;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
class AnimalType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('nom', TextType::class, [
"label"=>"Nom : ",
"required"=>true
])
->add('genre', ChoiceType::class, [
"choices"=> [
'Femelle'=>'Femelle',
'Mâle'=>'Male',
],
"label"=>"Genre : ",
"required"=>true,
"expanded"=>true,
])
->add('entente_chien', ChoiceType::class, [
"choices"=> [
'Oui'=>'1',
'Non'=>'0',
"N/A"=>"2",
],
"label"=>"Entente avec les chiens : ",
"required"=>true,
// "expanded"=>true,
])
->add('entente_chat', ChoiceType::class, [
"choices"=> [
'Oui'=>'1',
'Non'=>'0',
"N/A"=>"2",
],
"label"=>"Entente avec les chats : ",
"required"=>true,
// "expanded"=>true
])
->add('entente_enfant', ChoiceType::class, [
"choices"=> [
'Oui'=>'1',
'Non'=>'0',
"N/A"=>"2",
],
"label"=>"Entente avec les enfants : ",
"required"=>true,
// "expanded"=>true
])
->add('titre', TextareaType::class, [
"label"=>"Titre : ",
"required"=>true,
])
->add('description', CKEditorType::class, [
"label"=>'Description : ',
"required"=>true,
])
->add('Espece', EntityType::class, [
"class" => Espece::class,
"label" => "Espèce : ",
"required" => true,
"choice_label" => "nom",
"placeholder"=>"----",
// "empty_data"=>null,
// "by_reference" => false
])
->add('race', EntityType::class,[
"class"=> Race::class,
"label"=>"Race : ",
"required"=>true,
"choice_label" => 'nom',
"choice_attr"=>ChoiceList::attr($this, function(?Race $race){
return $race ? ['data-espece'=>$race->getEspeceId()] : [];
}),
"placeholder"=>"----",
// "empty_data"=>null,
])
->add('tags', EntityType::class, [
'class' => Tags::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c');
},
'label' => 'Caractéristiques :',
'expanded' => true,
'multiple' => true,
'choice_label' => 'caracteristique',
'by_reference' => false,
"required"=>true,
"empty_data"=>null,
])
->add('age', ChoiceType::class, [
"label"=>"Âge : ",
"placeholder"=>"----",
'choices'=> [...]
],
"required"=>true,
])
->add('enteredAt', DateType::class, [
'label'=>'Date d\'entrée : ',
'widget'=>'choice',
'input'=>'datetime_immutable',
'format'=>'dd / MM / yyyy',
'required'=>true,
]);
// If the animal is not created
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event){
// Récupère les données du formulaire si l'article est déjà rempli
$animal = $event->getData();
// Récupère le formulaire
$form = $event->getForm();
// Is the animal already exist
if(!$animal || null === $animal->getId()){
$form->add('animalImages', CollectionType::class, [
'entry_type'=>AnimalImageType::class,
'allow_add'=>true,
'allow_delete'=>true,
'delete_empty'=>true,
'prototype'=>true,
'by_reference'=>false,
'mapped'=>false,
]);
} else {
$form->add('animalImages', CollectionType::class, [
'entry_type'=>AnimalImageType::class,
'allow_add'=>true,
'allow_delete'=>true,
'delete_empty'=>true,
'prototype'=>true,
'by_reference'=>false,
]);
}
});
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Animal::class,
]);
}
public function preUpdate(object $animal): void
{
$this->animalManager->updateCanonicalFields($animal);
}
}
以及我在 vich_uploader.yaml 上的映射
animal:
uri_prefix: /images/animaux
upload_destination: "%kernel.project_dir%/public/images/animaux"
directory_namer:
service: vich_uploader.namer_directory_property
options: { property: "animal.nom", transliterate: true }
namer: Vich\UploaderBundle\Naming\SmartUniqueNamer
inject_on_load: false
delete_on_update: true
delete_on_remove: true
最后,我的实体文件(名为:Animal)
<?php
namespace App\Entity;
use App\Entity\AnimalImage;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\AnimalRepository;
use Vich\UploaderBundle\Entity\File;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: AnimalRepository::class)]
class Animal
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'animals')]
#[ORM\JoinColumn(nullable: false)]
private ?Espece $espece = null;
#[ORM\ManyToOne(inversedBy: 'animals')]
#[ORM\JoinColumn(nullable: false)]
private ?Race $race = null;
#[ORM\Column(length: 50)]
private ?string $nom = null;
#[ORM\Column(length: 30)]
private ?string $genre = null;
#[ORM\Column(nullable: true)]
private ?int $entente_chien = null;
#[ORM\Column(nullable: true)]
private ?int $entente_chat = null;
#[ORM\Column(nullable: true)]
private ?int $entente_enfant = null;
#[ORM\Column(length: 200, nullable: true)]
#[Assert\Length(
max: 38,
maxMessage: 'Le titre ne peut excéder {{limit}} caractères',
)]
private ?string $titre = null;
#[ORM\Column(type: Types::TEXT)]
private ?string $description = null;
#[ORM\OneToMany(mappedBy: 'animal', targetEntity: AnimalImage::class, orphanRemoval: true, cascade: ['persist'])]
private Collection $animalImages;
#[ORM\Column(length: 100)]
private ?string $age = null;
#[ORM\Column]
private ?\DateTimeImmutable $enteredAt = null;
#[ORM\ManyToMany(targetEntity: Tags::class, inversedBy: 'animals')]
private Collection $tags;
public function __construct()
{
$this->animalImages = new ArrayCollection();
$this->tags = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEspece(): ?Espece
{
return $this->espece;
}
public function setEspece(?Espece $espece): self
{
$this->espece = $espece;
return $this;
}
public function getRace(): ?race
{
return $this->race;
}
public function setRace(?race $race): self
{
$this->race = $race;
return $this;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getGenre(): ?string
{
return $this->genre;
}
public function setGenre(string $genre): self
{
$this->genre = $genre;
return $this;
}
public function getEntenteChien(): ?int
{
return $this->entente_chien;
}
public function setEntenteChien(?int $entente_chien): self
{
$this->entente_chien = $entente_chien;
return $this;
}
public function getEntenteChat(): ?int
{
return $this->entente_chat;
}
public function setEntenteChat(?int $entente_chat): self
{
$this->entente_chat = $entente_chat;
return $this;
}
public function getEntenteEnfant(): ?int
{
return $this->entente_enfant;
}
public function setEntenteEnfant(?int $entente_enfant): self
{
$this->entente_enfant = $entente_enfant;
return $this;
}
public function getTitre(): ?string
{
return $this->titre;
}
public function setTitre(?string $titre): self
{
$this->titre = $titre;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
/**
* @return Collection<int, AnimalImage>
*/
public function getAnimalImages(): Collection
{
return $this->animalImages;
}
/**
* Set the value of animalImages
*
* @return self
*/
public function setAnimalImages(?File $animalImages = null): void
{
$this->animalImages = $animalImages;
if(null!==$animalImages){
$this->updatedAt = new \DateTimeImmutable();
}
}
public function addAnimalImage(AnimalImage $animalImage): self
{
if (!$this->animalImages->contains($animalImage)) {
$this->animalImages[] = $animalImage;
$animalImage->setAnimal($this);
}
return $this;
}
public function removeAnimalImage(AnimalImage $animalImage): self
{
if ($this->animalImages->removeElement($animalImage)) {
// set the owning side to null (unless already changed)
if ($animalImage->getAnimal() === $this) {
$animalImage->setAnimal(null);
}
}
return $this;
}
public function getAge(): ?string
{
return $this->age;
}
public function setAge(string $age): self
{
$this->age = $age;
return $this;
}
public function getEnteredAt(): ?\DateTimeImmutable
{
return $this->enteredAt;
}
public function setEnteredAt(\DateTimeImmutable $enteredAt): self
{
$this->enteredAt = $enteredAt;
return $this;
}
/**
* @return Collection<int, Tags>
*/
public function getTags(): Collection
{
return $this->tags;
}
public function addTag(Tags $tag): self
{
if (!$this->tags->contains($tag)) {
$this->tags->add($tag);
}
return $this;
}
public function removeTag(Tags $tag): self
{
$this->tags->removeElement($tag);
return $this;
}
}
提前感谢您提供任何解决方案和帮助,在这个项目上工作了 1 个月,并在漫长的日子里解决了这个错误:/
编辑:尝试了很多“解决方案”并评论了一行,现在它可以工作了。 这是我的解决方案
{
if ($this->animalImages->removeElement($animalImage)) {
// set the owning side to null (unless already changed)
if ($animalImage->getAnimal() === $this) {
$fileName = $animalImage->getImageName();
$filesystem = new Filesystem();
$filesystem->remove('%kernel.project_dir%/public/images/animaux/'.$this->getNom().'/'. $fileName);
// COMMENTING THIS LINE MADE THE THING WORKS
// $animalImage->setAnimal(null);
}
}
return $this;
}
我刚刚遇到了这个问题,问题是需要遍历路径的对象没有在构造函数中初始化