Vich Uploader Bundle и Doctrine Карта наследования - PullRequest
0 голосов
/ 30 апреля 2020

Я создаю приложение Symfony 5 и использую Doctrine Inheritance Mapping, в частности наследование таблиц классов, чтобы разделить определенные свойства с несколькими сущностями и связать их. Symfony версия 5.0.7 и Vich Uploader Bundle 1.14.x-dev.

Следуя руководству Doctrines для наследования таблиц классов, я создал родительский класс ScopeObject, и этот класс использует Vich Uploader Связать, чтобы у всех подклассов было изображение аватара. Помимо проблем с Vich, Doctrine работает нормально. Обратите внимание, что я предполагаю, что это проблема Vich, а не проблема Doctrine.

Просто для справки, подклассы могут варьироваться от пользователя, заказа, продукта, каталога, и хотя у меня нет всех этих подклассов, проблема появилась на классах, которые у меня есть. Идея состоит в том, что каждый из моих различных подклассов может иметь изображение аватара.

Теперь я уже использовал Vich Uploader Bundle в другом месте моего приложения, и это сработало, и все еще работает отлично. Проблема возникла после того, как я реализовал ее в этом родительском классе, и я не уверен, что это проблема или я делаю что-то явно не так.

Я реализовал Vich, и он работал на контент, который у меня уже был в базе данных после миграции, что позволяет мне добавлять и удалять аватары для моих объектов. Однако когда я пытался создать новую сущность (у меня в настоящее время есть только Directory и User), я получаю следующее исключение

PropertyAccessor требуется граф объектов или массивов для работы, но он нашел тип " NULL "при попытке пройти путь" image.name "в свойстве" name ".

Чтобы временно устранить проблему, я изменил свой родительский класс ScopeObject, чтобы аннотация Vich UploadableField сохраняла fileNameProperty под новым свойством в моем родительском классе с именем imageNewName. Это сработало, но теперь у меня есть бесполезное поле в моей базе данных из унаследованного файла Vich.

Я провел некоторый поиск и пока единственное другое обнаружение этой ошибки, которое я обнаружил здесь было простое исправление, где они пропустили метод конструктора из сущности, которая у меня есть. Я пробовал конструктор как в родительском, так и в подклассе, но ни один из них не работал, и я получил то же исключение. Метод конструктора для Vich:

public function __construct()
{
    $this->image = new EmbeddedFile();
}

Ниже представлен мой родительский класс, обратите внимание, что у меня уже есть свойство с именем name, что, как я думал, может быть проблемой, но где я использую Vich в другом месте, моя сущность имеет Имя свойства там, и он отлично работает.

<?php

namespace App\Entity;

use App\Entity\Attachments\Attachments;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Vich\UploaderBundle\Entity\File as EmbeddedFile;

/**
 * @ORM\Entity
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="entity", type="string")
 * @ORM\DiscriminatorMap({
 *     "scopeObject" = "ScopeObject",
 *     "directory" = "Directory",
 *     "user" = "\App\Entity\User\User"
 * })
 *
 * @Vich\Uploadable
 */
class ScopeObject
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Actions", mappedBy="scopeObject", orphanRemoval=true)
     */
    private $action;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Attachments\Attachments", mappedBy="scopeObject", orphanRemoval=true)
     */
    private $attachments;

    /**
     * NOTE: This is not a mapped field of entity metadata, just a simple property.
     *
     * @Vich\UploadableField(mapping="attachment", fileNameProperty="imageNewName", size="image.size", mimeType="image.mimeType", originalName="image.originalName", dimensions="image.dimensions")
     *
     * @var File|null
     */
    private $imageFile;

    /**
     * @ORM\Embedded(class="Vich\UploaderBundle\Entity\File")
     *
     * @var EmbeddedFile
     */
    private $image;

    /**
     * @ORM\Column(type="string", nullable=true)
     */
    private $imageNewName;

    /**
     * @ORM\Column(type="datetime", nullable=true)
     *
     * @var \DateTimeInterface|null
     */
    private $updatedAt;

    public function __construct()
    {
        $this->image = new EmbeddedFile();
        $this->action = new ArrayCollection();
        $this->attachments = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return Collection|Actions[]
     */
    public function getAction(): Collection
    {
        return $this->action;
    }

    public function addAction(Actions $action): self
    {
        if (!$this->action->contains($action)) {
            $this->action[] = $action;
            $action->setObject($this);
        }

        return $this;
    }

    public function removeAction(Actions $action): self
    {
        if ($this->action->contains($action)) {
            $this->action->removeElement($action);
            // set the owning side to null (unless already changed)
            if ($action->getObject() === $this) {
                $action->setObject(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|Attachments[]
     */
    public function getAttachments(): Collection
    {
        return $this->attachments;
    }

    public function addAttachment(Attachments $attachment): self
    {
        if (!$this->attachments->contains($attachment)) {
            $this->attachments[] = $attachment;
            $attachment->setScopeObject($this);
        }

        return $this;
    }

    public function removeAttachment(Attachments $attachment): self
    {
        if ($this->attachments->contains($attachment)) {
            $this->attachments->removeElement($attachment);
            // set the owning side to null (unless already changed)
            if ($attachment->getScopeObject() === $this) {
                $attachment->setScopeObject(null);
            }
        }

        return $this;
    }

    /**
     * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
     * of 'UploadedFile' is injected into this setter to trigger the  update. If this
     * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
     * must be able to accept an instance of 'File' as the bundle will inject one here
     * during Doctrine hydration.
     *
     * @param File|UploadedFile|null $imageFile
     */
    public function setImageFile(?File $imageFile = null)
    {
        $this->imageFile = $imageFile;

        if (null !== $imageFile) {
            // It is required that at least one field changes if you are using doctrine
            // otherwise the event listeners won't be called and the file is lost
            $this->updatedAt = new \DateTimeImmutable();
        }
    }

    public function getImageFile(): ?File
    {
        return $this->imageFile;
    }

    public function setImage(EmbeddedFile $image): void
    {
        $this->image = $image;
    }

    public function getImage(): ?EmbeddedFile
    {
        return $this->image;
    }

    public function setImageNewName(EmbeddedFile $imageNewName): void
    {
        $this->imageNewName = $imageNewName;
    }

    public function getImageNewName(): ?EmbeddedFile
    {
        return $this->imageNewName;
    }
}

Ниже приведен мой подкласс Справочника. По сути, это похоже на телефонный справочник, в котором хранятся контакты, клиенты, поставщики и т. Д. c, поэтому он довольно многоцелевой, и на него также можно ссылаться на ManyToMany через свойства контактов и каталогов. Обратите внимание, что исключение также генерируется в моем объекте User, который также расширяет ScopeObject

<?php

namespace App\Entity;

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

/**
 * @ORM\Entity(repositoryClass="App\Repository\DirectoryRepository")
 */
class Directory extends ScopeObject
{
    /**
     * @ORM\Column(type="array", nullable=true)
     */
    private $phone = [];

    /**
     * @ORM\Column(type="array", nullable=true)
     */
    private $email = [];

    /**
     * @ORM\Column(type="text", nullable=true)
     */
    private $street;

    /**
     * @ORM\Column(type="string", length=100, nullable=true)
     */
    private $city;

    /**
     * @ORM\Column(type="string", length=100, nullable=true)
     */
    private $county;

    /**
     * @ORM\Column(type="string", length=12, nullable=true)
     */
    private $postcode;

    /**
     * @ORM\Column(type="string", length=100, nullable=true)
     */
    private $country;

    /**
     * @ORM\Column(type="array", nullable=true)
     */
    private $address = [];

    /**
     * @ORM\Column(type="string", length=20, nullable=true)
     */
    private $accountNumber;

    /**
     * @ORM\Column(type="string", length=25, nullable=true)
     */
    private $vat;

    /**
     * @ORM\Column(type="boolean", nullable=true)
     */
    private $active;

    /**
     * @ORM\Column(type="boolean", nullable=true)
     */
    private $cashCustomer;

    /**
     * @ORM\Column(type="boolean", nullable=true)
     */
    private $onStop;

    /**
     * @ORM\Column(type="string", length=100, nullable=true)
     */
    private $priceCategory;

    /**
     * @ORM\Column(type="string", length=10, nullable=true)
     */
    private $discountCategory;

    /**
     * @ORM\Column(type="boolean", nullable=true)
     */
    private $bookableResource;

    /**
     * @ORM\Column(type="integer", nullable=true)
     */
    private $resourceLocation;

    /**
     * @ORM\Column(type="array", nullable=true)
     */
    private $type = [];

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $title;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $department;

    /**
     * @ORM\Column(type="string", length=100, nullable=true)
     */
    private $lawfulBasis;

    /**
     * @ORM\Column(type="string", length=50, nullable=true)
     */
    private $purchaseTax;

    /**
     * @ORM\Column(type="integer", nullable=true)
     */
    private $dayCost;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Directory", inversedBy="directories")
     */
    private $contacts;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Directory", mappedBy="contacts")
     */
    private $directories;

    public function __construct()
    {
        $this->contacts = new ArrayCollection();
        $this->directories = new ArrayCollection();
        $this->setActive(true);
    }

    public function getPhone(): ?array
    {
        return $this->phone;
    }

    public function setPhone(?array $phone): self
    {
        $this->phone = $phone;

        return $this;
    }

    public function getEmail(): ?array
    {
        return $this->email;
    }

    public function setEmail(?array $email): self
    {
        $this->email = $email;

        return $this;
    }

    public function getStreet(): ?string
    {
        return $this->street;
    }

    public function setStreet(?string $street): self
    {
        $this->street = $street;

        return $this;
    }

    public function getCity(): ?string
    {
        return $this->city;
    }

    public function setCity(?string $city): self
    {
        $this->city = $city;

        return $this;
    }

    public function getCounty(): ?string
    {
        return $this->county;
    }

    public function setCounty(?string $county): self
    {
        $this->county = $county;

        return $this;
    }

    public function getPostcode(): ?string
    {
        return $this->postcode;
    }

    public function setPostcode(?string $postcode): self
    {
        $this->postcode = $postcode;

        return $this;
    }

    public function getCountry(): ?string
    {
        return $this->country;
    }

    public function setCountry(?string $country): self
    {
        $this->country = $country;

        return $this;
    }

    public function getAddress(): ?array
    {
        return $this->address;
    }

    public function setAddress(?array $address): self
    {
        $this->address = $address;

        return $this;
    }

    public function getAccountNumber(): ?string
    {
        return $this->accountNumber;
    }

    public function setAccountNumber(?string $accountNumber): self
    {
        $this->accountNumber = $accountNumber;

        return $this;
    }

    public function getVat(): ?string
    {
        return $this->vat;
    }

    public function setVat(?string $vat): self
    {
        $this->vat = $vat;

        return $this;
    }

    public function getActive(): ?bool
    {
        return $this->active;
    }

    public function setActive(?bool $active): self
    {
        $this->active = $active;

        return $this;
    }

    public function getCashCustomer(): ?bool
    {
        return $this->cashCustomer;
    }

    public function setCashCustomer(?bool $cashCustomer): self
    {
        $this->cashCustomer = $cashCustomer;

        return $this;
    }

    public function getOnStop(): ?bool
    {
        return $this->onStop;
    }

    public function setOnStop(?bool $onStop): self
    {
        $this->onStop = $onStop;

        return $this;
    }

    public function getPriceCategory(): ?string
    {
        return $this->priceCategory;
    }

    public function setPriceCategory(?string $priceCategory): self
    {
        $this->priceCategory = $priceCategory;

        return $this;
    }

    public function getDiscountCategory(): ?string
    {
        return $this->discountCategory;
    }

    public function setDiscountCategory(string $discountCategory): self
    {
        $this->discountCategory = $discountCategory;

        return $this;
    }

    public function getBookableResource(): ?bool
    {
        return $this->bookableResource;
    }

    public function setBookableResource(?bool $bookableResource): self
    {
        $this->bookableResource = $bookableResource;

        return $this;
    }

    public function getResourceLocation(): ?int
    {
        return $this->resourceLocation;
    }

    public function setResourceLocation(?int $resourceLocation): self
    {
        $this->resourceLocation = $resourceLocation;

        return $this;
    }

    public function getType(): ?array
    {
        return $this->type;
    }

    public function setType(?array $type): self
    {
        $this->type = $type;

        return $this;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(?string $title): self
    {
        $this->title = $title;

        return $this;
    }

    public function getDepartment(): ?string
    {
        return $this->department;
    }

    public function setDepartment(?string $department): self
    {
        $this->department = $department;

        return $this;
    }

    public function getLawfulBasis(): ?string
    {
        return $this->lawfulBasis;
    }

    public function setLawfulBasis(?string $lawfulBasis): self
    {
        $this->lawfulBasis = $lawfulBasis;

        return $this;
    }

    public function getPurchaseTax(): ?string
    {
        return $this->purchaseTax;
    }

    public function setPurchaseTax(?string $purchaseTax): self
    {
        $this->purchaseTax = $purchaseTax;

        return $this;
    }

    public function getDayCost(): ?int
    {
        return $this->dayCost;
    }

    public function setDayCost(?int $dayCost): self
    {
        $this->dayCost = $dayCost;

        return $this;
    }

    /**
     * @return Collection|self[]
     */
    public function getContacts(): Collection
    {
        return $this->contacts;
    }

    public function addContact(self $contact): self
    {
        if (!$this->contacts->contains($contact)) {
            $this->contacts[] = $contact;
        }

        return $this;
    }

    public function removeContact(self $contact): self
    {
        if ($this->contacts->contains($contact)) {
            $this->contacts->removeElement($contact);
        }

        return $this;
    }

    /**
     * @return Collection|self[]
     */
    public function getDirectories(): Collection
    {
        return $this->directories;
    }

    public function addDirectory(self $directory): self
    {
        if (!$this->directories->contains($directory)) {
            $this->directories[] = $directory;
            $directory->addContact($this);
        }

        return $this;
    }

    public function removeDirectory(self $directory): self
    {
        if ($this->directories->contains($directory)) {
            $this->directories->removeElement($directory);
            $directory->removeContact($this);
        }

        return $this;
    }
}

Для моего временного исправления я изменил отображение imageFile в ScopeObject следующим образом, но, как упоминалось ранее, это оставляет меня с избыточное поле в базе данных:

/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* @Vich\UploadableField(mapping="attachment", fileNameProperty="imageNewName", size="image.size", mimeType="image.mimeType", originalName="image.originalName", dimensions="image.dimensions")
*
* @var File|null
*/
private $imageFile;

/**
* @ORM\Column(type="string", nullable=true)
*/
private $imageNewName;

public function setImageNewName(EmbeddedFile $imageNewName): void
{
    $this->imageNewName = $imageNewName;
}

public function getImageNewName(): ?EmbeddedFile
{
    return $this->imageNewName;
}

Дайте мне знать, если вы хотите увидеть больше кода, это мой первый вопрос (хотя переполнение стека было для меня отличным ресурсом), поэтому я надеюсь, что я выполнили необходимые критерии.

...