2

I have four pictures on my homepage banner and I'd like to be able to update them every now and then using a Symfony form.

I set up an Image Entity exactly as described in the Vich Uploader documentation (with updated At-Entity and everything)

    <?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Image
*
* @ORM\Table(name="app_image")
* @ORM\Entity
* @Vich\Uploadable
*/
class Image {

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

  /**
   * @Vich\UploadableField(mapping="dashboard", fileNameProperty="imageName", size="imageSize")
   *
   * @var File
   */
  private $imageFile;

  /**
    * @ORM\Column(type="integer")
    *
    * @var integer
    */
   private $imageSize;

   /**
    * @var string
    * @ORM\Column(type="string", nullable=false)
    */
   private $imageName;

   /**
   * @ORM\Column(type="datetime")
   *
   * @var \DateTime
   */
  private $updatedAt;

      /**
     * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
     */
    public function setImageFile(?File $image = null): void
    {
        $this->imageFile = $image;

        if (null !== $image) {

            $this->updatedAt = new \DateTimeImmutable();
        }
    }

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

    public function setImageName(?string $imageName): void
    {
        $this->imageName = $imageName;
    }

    public function getImageName(): ?string
    {
        return $this->imageName;
    }

    public function setImageSize(?int $imageSize): void
    {
        $this->imageSize = $imageSize;
    }

    public function getImageSize(): ?int
    {
        return $this->imageSize;
    }


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

    /**
     * Set updatedAt
     *
     * @param \DateTime $updatedAt
     *
     * @return Image
     */
    public function setUpdatedAt($updatedAt)
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    /**
     * Get updatedAt
     *
     * @return \DateTime
     */
    public function getUpdatedAt()
    {
        return $this->updatedAt;
    }
}

Since the four images already exist, I only added an action to update the images to my Controller

  /**
  * @Route("/uploadImage/{id}", name="appBundle_upload_image", requirements={"id" = "\d+"})
  * @Security("is_granted('ROLE_ATTACHEMENT_ADMIN')")
  */
  public function uploadAction(Request $request, $id){
    $repository = $this->getDoctrine()->getRepository('AppBundle:Image');
    $em = $this->getDoctrine ()->getManager ();
    $image = $repository->findOneById($id);
    $form = $this->createForm(UploadImageType::class, $image);
    $form->handleRequest($request);
    if ($form->isValid() ) {
      $em->flush();
      return $this->redirectToRoute('index');
    }
    return $this->render('AppBundle:Image:upload.html.twig', array(
      'form' => $form->createView(),
      'image' => $image,
    ));
  }

I already have some pictures in my database table, so I can access the route to my controller action, the form that gets rendered contains simply this:

  public function buildForm(FormBuilderInterface $builder, array $options): void
{

    $builder->add('imageFile', VichImageType::class, [
        'required' => false,
        'allow_delete' => false,
        'download_label' => '...',
        'download_uri' => true,
        'image_uri' => true,
    ]);
}

So as you can see, I didn't change a lot yet from the basic documentation. Anyway, when going to that route with e.g. ID 7, picture 7 is displayed under the download_uri and when dumping imageName or UpdatedAt, it's the same data as shown in my database table. But when I then try to upload a new picture which should replace the old one, I get an error saying

Entity AppBundle\Entity\Image@0000000075aa1d6c00000000789d890b is not managed. An entity is managed if its fetched from the database or registered as new through EntityManager#persist

or sometimes it goes through like a miracle and I get redirected, but nothing's changed in my database table, so the old picture didn't get replaced or anything.

I've search a lot and found a lot of solutions for the Entity not managed error, or not updating because no property changed. But none of them actually solved my problem.

Do you see anything in my code that could cause the problem?

UPDATE

I added @nifr 's setImageFile method to my Image Entity:

    /**
* @param File|UploadedFile|null $imageFile
*
* @return $this
*/
public function setImageFile(File $imageFile = null)
{
  $this->imageFile = $imageFile;
  dump($imageFile);
  if ($imageFile instanceof UploadedFile) {
    $this->setFileSize($imageFile->getClientSize());
    $this->setMimeType($imageFile->getClientMimeType());
    $this->setOriginalName($imageFile->getClientOriginalName());
    $this->updatedAt = new \DateTime();
  }

  return $this;
}

when dumping within the if condition, it's doing nothing, so apparently it doesn't recognize $imageFile as an instance of UploadedFile. However, when dumping before that (like in the copied code) it's dumping the following infos:

 UploadedFile {#14 ▼
  -test: false
  -originalName: "Route.png"
  -mimeType: "image/png"
  -size: 13218
  -error: 0
  path: "/tmp"
  filename: "phpQhWrTd"
  basename: "phpQhWrTd"
  pathname: "/tmp/phpQhWrTd"
  extension: ""
  realPath: "/tmp/phpQhWrTd"
  aTime: 2018-02-05 19:03:12
  mTime: 2018-02-05 19:03:12
  cTime: 2018-02-05 19:03:12
  inode: 311536
  size: 13218
  perms: 0100600
  owner: 1001
  group: 33
  type: "file"
  writable: true
  readable: true
  executable: false
  file: true
  dir: false
  link: false
}

So apparently it IS from "UploadedFile". Any ideas?

Also what I tried, is dumping within my Controller:

 public function uploadAction(Request $request, $id){
    $repository = $this->getDoctrine()->getRepository('AppBundle:Image');
    $em = $this->getDoctrine ()->getManager ();
    $image = $repository->findOneById($id);

    dump($image);

    $form = $this->createForm(UploadImageType::class, $image);
    $form->handleRequest($request);
    if ($form->isValid() && $form->isValid() ) {

    dump($image->getImageName());

      $em->flush();
      return $this->redirectToRoute('index');
    }
    return $this->render('AppBundle:Image:upload.html.twig', array(
      'form' => $form->createView(),
      'image' => $image,
    ));
  }

Having the dump in the imageFile Setter I realized, that my dump in the controller contains the "old" imageName e.g. Name A but the Setter contains the "new" image name (from the image that's supposed to replace the old one), so actually the correct one. But it's never coming to an update.

sonja
  • 924
  • 1
  • 21
  • 52
  • what is the schema structure for your images, e.g the `describe images` etc to your question – sakhunzai Feb 02 '18 at 10:59
  • I added some code that - while it doesn't directly answer your question - might help other users having problems with updating files via `vich-uploader-bundle` in the future. Did you manage to resolve your issue? – Nicolai Fröhlich Feb 02 '18 at 12:22
  • @sakhunzai sorry, I think I didn't get your question 100%, could you try to ask it again, differently? – sonja Feb 05 '18 at 13:21
  • @nifr no I didn't resolve my issue yet and would still be very happy about help! – sonja Feb 05 '18 at 13:21
  • I was looking if you have `updatedAt` column in your database table, as nifr answered below – sakhunzai Feb 06 '18 at 05:41
  • After looking at your comments below I think you need to confirm that the ID you used to update the picture is really valid by querying the db directly e.g `select * from pictures where id=7` ,it seems you are updating an entity that does not exist in db – sakhunzai Feb 06 '18 at 05:44
  • @sakhunzai thanks for your answer. Yes, I have the updatedAt column. Could you give me an example where you'd check for the ID in my code? :) – sonja Feb 06 '18 at 14:02
  • if you look into error you are getting its clear that the picture you are updating is not what you are thinking it is `Entity is not managed` means probably you are updating an image that is new ( id=0) or that does not exists . Some how your ids does not match your dbs records – sakhunzai Feb 11 '18 at 19:11
  • To debug take backup of your data , remove all images ,also rest image/pictures table (`truncate images` or whatever its name is), add new image, and try to update that. – sakhunzai Feb 11 '18 at 19:14
  • @sakhunzai I did as you said, emptied averything and uploaded a new picture. It got ID 1. I then went to **updateImage/1** I dumped the object $image and that was the output: Image {#3155 ▼ -id: 1 -imageFile: null -imageSize: 34703 -imageName: "5a7f9db650f78_EntityType.png" -updatedAt: DateTime {#3153 ▼ +"date": "2018-02-11 02:34:46.000000" +"timezone_type": 3 +"timezone": "Europe/Berlin" } } so imageFile was null again.. and when trying to update the object I received the same error – sonja Feb 12 '18 at 14:34
  • @sakhunzai so one issue is definitely the imageFile setter but I don't see where I made a mistake. I did everything as documented .. – sonja Feb 12 '18 at 14:47
  • I was just thinking, the error says Entity AppBundle\Entity\Image@000000007c0811ec000000004a4b5a0e is not managed... you are right that shows that it's not the correct entity. I don't see where the **@000 ...** part is coming from or how to change it? @sakhunzai – sonja Feb 12 '18 at 15:03
  • sometimes cache might be issue , rest cache also – sakhunzai Feb 12 '18 at 16:20
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164983/discussion-between-sakhunzai-and-sonja). – sakhunzai Feb 12 '18 at 16:24

1 Answers1

0

This is not the answer for the unmanaged entity error you're getting.

In order to ensure an image gets updated correctly you need to have a method like this in your entity:

use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;

/** @var \DateTime|null */
protected $updatedAt;

/**
 * @param File|UploadedFile|null $file
 *
 * @return $this
 */
public function setFile(File $file = null)
{
    $this->file = $file;

    if ($file instanceof UploadedFile) {
        $this->setFileSize($file->getClientSize());
        $this->setMimeType($file->getClientMimeType());
        $this->setOriginalName($file->getClientOriginalName());
        $this->updatedAt = new \DateTime();
    }

    return $this;
}
Nicolai Fröhlich
  • 51,330
  • 11
  • 126
  • 130
  • I was thinking same `updatedAt` this should be the answer – sakhunzai Feb 05 '18 at 07:37
  • But, I have a **setImageFile** method in my entity where the $updatedAt get's defined. When using the **instanceOf**, it's not working.. – sonja Feb 05 '18 at 13:22
  • when using your method, it submits, but nothing changes in my database. So setFile isn't doing anything at all.. the check for instanceOf UploadedFile is not leading to true – sonja Feb 05 '18 at 13:28
  • Are you sure that `setFile` is called at all? – Nicolai Fröhlich Feb 05 '18 at 14:02
  • @nifr it is called, but when I e.g. put a dump after the "instanceof" check, it doesn't dump, so it's not going into that if condition.. when dumping $file before it goes into the if condition, I get the infos, I just added to my question – sonja Feb 05 '18 at 18:00
  • Also, when looking into the dump($image) from my controller, I can see that imageFile is null. Why is that? – sonja Feb 05 '18 at 18:21