Symfony3,用于收集图像的编辑表格

时间:2017-02-21 13:18:52

标签: php forms symfony

我正在关注这些指南:File upload tutorialCollection form type guide。一切都很好,但“编辑”动作。在“编辑”操作中正确显示图像集合,但当我尝试提交表单(不添加任何新图像)时,我收到错误。

  

在null

上调用成员函数guessExtension()

这意味着集合中的图像具有空值,而不是UploadedFile。

我经常搜索并阅读许多关于处理带有学说的图像集的问题,但仍然没有线索。

如下问题:

所以问题是如何处理编辑图像?也需要搬迁。

控制器编辑操作

public function editAction(Request $request, Stamp $stamp)
{
    if (!$stamp) {
        throw $this->createNotFoundException('No stamp found');
    }
    $form = $this->createForm(AdminStampForm::class, $stamp);

    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        /** @var Stamp $stamp */
        $stamp = $form->getData();
        foreach ($stamp->getImages() as $image) {
            $fileName = md5(uniqid()) . '.' . $image->getName()->guessExtension();
            /** @var $image StampImage */
            $image->getName()->move(
                $this->getParameter('stamps_images_directory'),
                $fileName
            );
            $image->setName($fileName)->setFileName($fileName);
        }
        $em = $this->getDoctrine()->getManager();
        $em->persist($stamp);
        $em->flush();

        $this->addFlash('success', 'Successfully edited a stamp!');

        return $this->redirectToRoute('admin_stamps_list');
    }

    return [
        'form' => $form->createView(),
    ];
}

实体

<?php

namespace AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\JoinTable;
use Doctrine\ORM\Mapping\ManyToMany;

/**
 * Class Stamp
 * @package AppBundle\Entity
 *
 *
 * @ORM\Entity(repositoryClass="AppBundle\Repository\StampRepository")
 * @ORM\Table(name="stamp")
 * @ORM\HasLifecycleCallbacks()
 */
class Stamp
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    private $id;

    //      * @ORM\OneToMany(targetEntity="AppBundle\Entity\StampImage", mappedBy="id", cascade={"persist", "remove"})

    /**
     * @ManyToMany(targetEntity="AppBundle\Entity\StampImage", cascade={"persist"})
     * @JoinTable(name="stamps_images",
     *      joinColumns={@JoinColumn(name="stamp_id", referencedColumnName="id")},
     *      inverseJoinColumns={@JoinColumn(name="image_id", referencedColumnName="id", unique=true)}
     *      )     */
    private $images;

    /**
     * @ORM\Column(type="datetime")
     */
    private $createdAt;

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

    public function __construct()
    {
        $this->images = new ArrayCollection();
    }

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

    public function addImage(StampImage $image)
    {
        $this->images->add($image);
    }

    public function removeImage(StampImage $image)
    {
        $this->images->removeElement($image);
    }

    /**
     * @return mixed
     */
    public function getImages()
    {
        return $this->images;
    }

    /**
     * @param mixed $images
     *
     * @return $this
     */
    public function setImages($images)
    {
        $this->images = $images;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }

    /**
     * @param mixed $createdAt
     *
     * @return Stamp
     */
    public function setCreatedAt($createdAt)
    {
        $this->createdAt = $createdAt;

        return $this;
    }

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

    /**
     * @param mixed $updatedAt
     *
     * @return Stamp
     */
    public function setUpdatedAt($updatedAt)
    {
        $this->updatedAt = $updatedAt;

        return $this;
    }

    /**
     * @ORM\PrePersist
     * @ORM\PreUpdate
     */
    public function updateTimestamps()
    {
        $this->setUpdatedAt(new \DateTime('now'));

        if (null == $this->getCreatedAt()) {
            $this->setCreatedAt(new \DateTime());
        }
    }
}

StampImage实体

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity as UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Class StampImage
 * @package AppBundle\Entity
 *
 * @ORM\Entity()
 * @ORM\Table(name="stamp_image")
 * @UniqueEntity(fields={"name"}, message="This file name is already used.")
 */
class StampImage
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    private $id;

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

    private $name;

    /**
     * @ORM\Column(type="string")
     */
    private $fileName;

    /**
     * @return mixed
     */
    public function getFileName()
    {
        return $this->fileName;
    }

    /**
     * @param mixed $fileName
     *
     * @return StampImage
     */
    public function setFileName($fileName)
    {
        $this->fileName = $fileName;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param mixed $name
     *
     * @return StampImage
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

}

主要实体表格

<?php

namespace AppBundle\Form;

use AppBundle\Entity\Stamp;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class AdminStampForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('images', CollectionType::class, [
                'entry_type' => AdminStampImageForm::class,
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
            ])
        ;

    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Stamp::class,
        ]);
    }

    public function getName()
    {
        return 'app_bundle_admin_stamp_form';
    }
}

图片格式

<?php

namespace AppBundle\Form;

use AppBundle\Entity\StampImage;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class AdminStampImageForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', FileType::class, [
                'image_name' => 'fileName',
                'label' => false,
                'attr' => [
                ],
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => StampImage::class,
            'required' => false
        ]);
    }

    public function getBlockPrefix()
    {
        return 'app_bundle_admin_stamp_image_form';
    }
}

文件类型扩展程序

- extends 'form_div_layout.html.twig'

- block file_widget
  - spaceless
    - if image_url is not null
      %img{:src => "#{image_url}"}
      %div{:style => "display: none;"}
        #{block('form_widget')}
    - else
      #{block('form_widget')}

2 个答案:

答案 0 :(得分:2)

1)Multiupload(但不是OneToMany)。

2)编辑上传的图片:

# AppBundle/Entity/Stamp
/**
 * @ORM\Column(type="string", length=255)
 */
private $image;

# AppBundle/Form/StampForm
->add('imageFile', FileType::class, [ //the $image property of Stamp entity class will store the path to the file, and this imageFile field will get the uploaded file
    'data_class' => null, //important!
])

#AppBundle/Controller/StampController
/**
 * @Route("/{id}/edit", name="stamp_edit")
 * @Method({"GET", "POST"})
 */
public function editAction(Request $request, Stamp $stamp) {
    $editForm = $this->createForm(StampType::class, $stamp, [
        'action'=>$this->generateUrl('stamp_edit',['id'=>$stamp->getId()]),
        'method'=>'POST'
    ]);
    $editForm->handleRequest($request);
    if($editForm->isSubmitted() && $form->isValid()) {
        $imageFile = $editForm->get('imageFile')->getData();
        if (null != $imageFile) { //this means that for the current record that needs to be edited, the user has chosen a different image
            //1. remove the old image
            $oldImg = $this->getDoctrine()->getRepository('AppBundle:Stamp')->find($stamp);
            $this->get('app.file_remove')->removeFile($oldImg->getImage());

            //2. upload the new image
            $img = $this->get('app.file_upload')->upload($imageFile);

            //3. update the db, replacing the path to the old file with the path to the new uploaded file
            $stamp->setImage($img);
            $this->getDoctrine()->getManager()->flush();

            //4. add a success flash, and anything else you need, and redirect to a route
        } else { //if the user has chosen to edit a different field (but not the image one)
            $this->getDoctrine()->getManager()->flush();
            //add flash message, and redirect to a route
        }
    }

    return $this->render(...);
}

#AppBundle/Services/FileRemove
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
class FileRemove {
    private $targetDir;
    public function __construct($targetDir) {
        $this->targetDir = $targetDir;
    }
    public function removeFile($path) {
        $fs = new Filesystem();
        $file = $this->targetDir . '/' . $path;
        try{
            if($fs->exists($file)){
                $fs->remove($file);
                return true;
            }
            return false;
        } catch(IOExceptionInterface $e){
            //log error for $e->getPath();
        }
    }
}

#app/config/services.yml
app.file_remove:
    class: AppBundle/Services/FileRemove
    arguments: ['%stamp_dir%']

#app/config/config.yml
parameters:
    stamp_dir: '%kernel.root_dir%/../web/uploads/stamps' //assuming this is how you've set up the upload directory

#AppBundle/Services/FileUpload
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileUpload{
    private $targetDir;
    public function __construct($targetDir) {
        $this->targetDir = $targetDir;
    }
    public function upload(UploadedFile $file) {
        $file_name = empty($file->getClientOriginalName()) ? md5(uniqid()).'.'.$file->guessExtension() : $file->getClientOriginalName();
        $file->move($this->targetDir, $file_name);
        return $file_name;
    }
}

#app/config/services.yml
app.file_upload:
    class: AppBundle\Services\FileUpload
    arguments: ['%stamp_dir%']

对不起打字错误,如果我的情况适合你的情况,请告诉我这是否有效。

答案 1 :(得分:0)

检查您是否在foreach之前上传了图片。

if ($form->isSubmitted() && $form->isValid()) {
    /** @var Stamp $stamp */
    $stamp = $form->getData();

    if(!empty($stamp->getImages()) && count($stamp->getImages()) > 0){   // Check uploaded image
        foreach ($stamp->getImages() as $image) {
            $fileName = md5(uniqid()) . '.' . $image->guessExtension();
            /** @var $image StampImage */
            $image->getName()->move(
                $this->getParameter('stamps_images_directory'),
                $fileName
            );
            $image->setName($fileName)->setFileName($fileName);
        }
    }
    $em = $this->getDoctrine()->getManager();
    $em->persist($stamp);
    $em->flush();

    $this->addFlash('success', 'Successfully edited a stamp!');

    return $this->redirectToRoute('admin_stamps_list');
}

更新#1

替换

$fileName = md5(uniqid()) . '.' . $image->getName()->guessExtension();

$fileName = md5(uniqid()) . '.' . $image->guessExtension();