doupingmao1903 2013-08-24 12:33
浏览 54

Symfony2 - 嵌入文件表单

I have two Entities

 - Kitchen
 - KitchenSubImage

Each kitchen has a main image but also has many sub images (KitchenSubImage).

I have implemented both the entities and their form types. At this moment I have the form displayed and have implemented everything from How to Handle File Uploads with Symfony2 to handle the file upload.

The issue I have is that I have no idea how to handle both file uploads at once. It's made more complicated by the fact that the kitchen can have many sub images.

I also have the following error at the top of the form when I submit the form:

This value should be of type PWD\WebsiteBundle\Entity\KitchenSubImage.

Controller

<?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(),
        ));
    }
}

Kitchen Entity

<?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 Entity

<?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条回答 默认 最新

  • duanbo6871 2013-08-24 13:22
    关注

    Welcome back. Kind of wish that you had taken my previous suggestion and gone though the blog/tags example. You are still having big picture issues with collections.

    In your kitchen entity, this is all wrong:

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

    It should be:

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

    See how a collection aka relation works in Doctrine?. Just like the bolg/tags example shows. As the form component processes the subImages collection, it will call addSubImage for each posted KitchenSubImage.

    The above change may or may not fix everything. Kind of doubt it. If not:

    Please tell me that you got the Kitchen form working before you added the sub image collection? You are able to load/store/retrieve the main image? If not, comment out $builder->add('subImage', 'collection', and focus on the kitchen entity.

    Once kitchen is working, add subImages back into the form but comment out the allow_add and report what happens.

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

    With respect to how the sub images are processed, I can understand some of the confusion. I have not implemented a collection of images my self. Might be some gotchas.

    I do know that your need to call upload on each sub image. upload is actually a somewhat misleading name. The file is already on your serve sitting in a tmp directory somewhere. Upload just moves it to a permanent location and stores the path in your entity.

    Start by trying this:

        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');
        }
    

    It might be better to loop on subImages in kitchen::upload but try it in the controller for now.

    评论

报告相同问题?

悬赏问题

  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目
  • ¥20 mysql架构,按照姓名分表
  • ¥15 MATLAB实现区间[a,b]上的Gauss-Legendre积分
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题
  • ¥15 linux驱动,linux应用,多线程