Symfony2 - 嵌入文件表单

时间:2013-08-24 12:33:27

标签: php forms symfony file-upload doctrine-orm

我有两个实体

 - Kitchen
 - KitchenSubImage

每个厨房都有一个主图像,但也有许多子图像(KitchenSubImage)。

我已经实现了实体及其表单类型。此时我显示了表单并实现了How to Handle File Uploads with Symfony2以处理文件上传的所有内容。

我遇到的问题是我不知道如何同时处理两个文件上传。由于厨房可以有许多子图像,因此更加复杂。

我提交表单时,表单顶部也出现以下错误:

  

此值应为PWD \ WebsiteBundle \ Entity \ KitchenSubImage类型。

控制器

<?php

namespace PWD\AdminBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

use PWD\WebsiteBundle\Entity\Kitchen;
use PWD\AdminBundle\Form\Type\KitchenType;

use PWD\WebsiteBundle\Entity\KitchenSubImage;
use PWD\AdminBundle\Form\Type\KitchenSubImageType;

class KitchenController extends Controller
{
    public function indexAction()
    {
        return 'index';
    }

    public function addAction(Request $request)
    {
        $kitchen = new Kitchen();
        $image = new KitchenSubImage();
        $kitchen->addSubImage($image);
        $form = $this->createForm(new KitchenType(), $kitchen);

        $form->handleRequest($request);

        if ($form->isValid()) {
            $kitchen->upload();
            return $this->render('PWDWebsiteBundle:Pages:home.html.twig');
        }

        return $this->render('PWDAdminBundle:Pages:form-test.html.twig', array(
            'form' => $form->createView(),
        ));
    }
}

厨房实体

<?php 

namespace PWD\WebsiteBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="kitchen")
 */
class Kitchen
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=100)
     * @Assert\NotBlank()
     */
    protected $name;

    /**
     * @ORM\Column(type="text")
     * @Assert\NotBlank()
     */
    protected $description;

    /**
     * @Assert\File(maxSize="6000000")
     * @Assert\Image(
     *     minWidth = 800,
     *     maxWidth = 800,
     *     minHeight = 467,
     *     maxHeight = 467
     * )
     */
    protected $mainImage;

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

    /**
     * @Assert\Type(type="PWD\WebsiteBundle\Entity\KitchenSubImage")
     * @ORM\OneToMany(targetEntity="KitchenSubImage", mappedBy="kitchen")
     */
    protected $subImage;

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

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

    public function setName($name)
    {
        $this->name = $name;
    }

    public function getDescription()
    {
        return $this->description;
    }

    public function setDescription($description)
    {
        $this->description = $description;
    }

    public function getMainImage()
    {
        return $this->mainImage;
    }

    public function setMainImage(UploadedFile $mainImage = null)
    {
        $this->mainImage = $mainImage;
    }

    public function getSubImage()
    {
        return $this->subImage;
    }

    public function setSubImage(KitchenSubImage $subImage = null)
    {
        $this->subImage = $subImage;
    }

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

    /**
     * Set mainImagePath
     *
     * @param string $mainImagePath
     * @return Kitchen
     */
    public function setMainImagePath($mainImagePath)
    {
        $this->mainImagePath = $mainImagePath;

        return $this;
    }

    /**
     * Get mainImagePath
     *
     * @return string 
     */
    public function getMainImagePath()
    {
        return $this->mainImagePath;
    }

    public function getAbsolutePath()
    {
        return null === $this->mainImagePath
            ? null
            : $this->getUploadRootDir().'/'.$this->mainImagePath;
    }

    public function getWebPath()
    {
        return null === $this->mainImagePath
            ? null
            : $this->getUploadDir().'/'.$this->mainImagePath;
    }

    public function getUploadRootDir()
    {
        return __DIR__.'/../../../../web/'.$this->getUploadDir();
    }

    public function getUploadDir()
    {
        return 'uploads/our-work';
    }

    public function upload()
    {
        if (null === $this->getMainImage()) {
            return;
        }

        $this->getMainImage()->move(
            $this->getUploadRootDir(),
            $this->getMainImage()->getClientOriginalName()
        );

        $this->mainImagePath = $this->getMainImage()->getClientOriginalName();

        $this->mainImage = null;
    }

    /**
     * Add subImage
     *
     * @param \PWD\WebsiteBundle\Entity\KitchenSubImage $subImage
     * @return Kitchen
     */
    public function addSubImage(\PWD\WebsiteBundle\Entity\KitchenSubImage $subImage)
    {
        $this->subImage[] = $subImage;
        $subImage->setKitchen($this); # used for persisting

        return $this;
    }

    /**
     * Remove subImage
     *
     * @param \PWD\WebsiteBundle\Entity\KitchenSubImage $subImage
     */
    public function removeSubImage(\PWD\WebsiteBundle\Entity\KitchenSubImage $subImage)
    {
        $this->subImage->removeElement($subImage);
    }
}

KitchenSubImage实体

<?php

namespace PWD\WebsiteBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="kitchenImages")
 */
class KitchenSubImage 
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @Assert\Image(
     *     minWidth = 800,
     *     maxWidth = 800,
     *     minHeight = 467,
     *     maxHeight = 467
     * )
     */
    public $image;

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

    /**
     * @ORM\ManyToOne(targetEntity="Kitchen", inversedBy="subImage")
     * @ORM\JoinColumn(name="kitchen_id", referencedColumnName="id")
     **/
    protected $kitchen;

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

    public function setImage(UploadedFile $image = null)
    {
        $this->image = $image;
    }

    public function getAbsolutePath()
    {
        return null === $this->imagePath
            ? null
            : $this->getUploadRootDir().'/'.$this->imagePath;
    }

    public function getWebPath()
    {
        return null === $this->imagePath
            ? null
            : $this->getUploadDir().'/'.$this->imagePath;
    }

    public function getUploadRootDir()
    {
        return __DIR__.'/../../../../web/'.$this->getUploadDir();
    }

    public function getUploadDir()
    {
        return 'uploads/our-work';
    }

    public function upload()
    {
        if (null === $this->getImage()) {
            return;
        }

        $this->getImage()->move(
            $this->getUploadRootDir(),
            $this->getImage()->getClientOriginalName()
        );

        $this->mainImagePath = $this->getImage()->getClientOriginalName();

        $this->mainImage = null;
    }

    /**
     * Set imagePath
     *
     * @param string $imagePath
     * @return KitchenSubImage
     */
    public function setImagePath($imagePath)
    {
        $this->imagePath = $imagePath;

        return $this;
    }

    /**
     * Get imagePath
     *
     * @return string 
     */
    public function getImagePath()
    {
        return $this->imagePath;
    }

    /**
     * Set kitchen
     *
     * @param \PWD\WebsiteBundle\Entity\Kitchen $kitchen
     * @return KitchenSubImage
     */
    public function setKitchen(\PWD\WebsiteBundle\Entity\Kitchen $kitchen = null)
    {
        $this->kitchen = $kitchen;

        return $this;
    }

    /**
     * Get kitchen
     *
     * @return \PWD\WebsiteBundle\Entity\Kitchen 
     */
    public function getKitchen()
    {
        return $this->kitchen;
    }

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

KitchenType:

<?php 

namespace PWD\AdminBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class KitchenType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name');
        $builder->add('description', 'textarea');
        $builder->add('mainImage', 'file');
        $builder->add('subImage', 'collection', array(
            'type' => new KitchenSubImageType(), 
            'label' => false,
            'allow_add' => true, 
            'by_reference' => false,
        ));
        $builder->add('submit', 'submit');
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'PWD\WebsiteBundle\Entity\Kitchen',
            'cascade_validation' => true,
        ));
    }

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

KitchenSubImageType:

<?php 

namespace PWD\AdminBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class KitchenSubImageType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('image', 'file', array('label' => 'Sub Images'));
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'PWD\WebsiteBundle\Entity\KitchenSubImage',
        ));
    }

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

1 个答案:

答案 0 :(得分:1)

欢迎回来。希望你已经采纳了我之前的建议,并通过博客/标签示例。你仍然对收藏品有很大的问题。

在你的厨房实体中,这是完全错误的:

protected $subImage;

public function getSubImage()
{
    return $this->subImage;
}
public function setSubImage(KitchenSubImage $subImage = null)
{
    $this->subImage = $subImage;
}

应该是:

protected $subImages;

public function getSubImages()
{
    return $this->subImages;
}
public function addSubImage(KitchenSubImage $subImage)
{
    $this->subImages[] = $subImage;
    $subImage->setKitchen($this);
}

了解一个集合aka关系如何在Doctrine中起作用?就像bolg / tags示例所示。当表单组件处理subImages集合时,它将为每个发布的KitchenSubImage调用addSubImage。

上述更改可能会也可能不会解决所有问题。有点怀疑。如果不是:

请在添加子图像集之前告诉我您的厨房表格是否正常工作?您可以加载/存储/检索主图像吗?如果没有,请注释掉$ builder-&gt; add('subImage','collection',并专注于厨房实体。

一旦厨房工作,将子图像添加回表单,但注释掉allow_add并报告发生的情况。

=============================================== ====

关于如何处理子图像,我可以理解一些混淆。我没有实现我自己的图像集合。可能是一些陷阱。

我知道您需要在每个子图像上调用上传。上传实际上是一个有点误导性的名称。该文件已经位于某个tmp目录中的服务器上。上传只是将其移动到永久位置并将路径存储在您的实体中。

首先尝试:

    if ($form->isValid()) {
        $kitchen->upload();
        foreach($kitchen->getSubImages() as $subImage)
        {
            $subImage->upload();
        }
        // really should redirect here but okay for now
        return $this->render('PWDWebsiteBundle:Pages:home.html.twig');
    }

最好循环使用kitchen :: upload中的subImages,但现在在控制器中尝试它。