Symfony2 FOSUserBundle文件上传(个人资料图片)提示?

4

我即将尝试在我正在使用FOSUserBundle的Symfony2项目中,为我的用户实体添加一个个人资料图片字段。我觉得这应该非常简单,然而我没有找到任何有用的文档来解释如何做到这一点。

目前,我计划在我的表单对象中添加一个未映射的字段(我已经从通用的ProfileEdit表单扩展了它),它可以接受上传的文件。然后,我将创建一个事件侦听器,用于处理表单、处理文件上传并将上传文件的路径(具有获取公共URL和绝对路径的方法)持久化到我的用户对象中,在响应flashbag中添加消息以表明它是成功还是失败。这是否真的是正确/最佳实践的方式?或者我漏掉了什么?难道这种功能真的还没有包含在FOSUserBundle中吗?如果是这样的话,我没有找到文档,但如果确实存在这样的功能,我会非常感激......

任何帮助/提示/小技巧都将不胜感激!


似乎另一种方法是将事件监听器添加到表单本身。有没有人有哪种更可取的经验? - Dom Weldon
1个回答

9
因此,事实证明我在回答这个问题时有些犯晕了:文件上传并不需要由FOSUserBundle来处理,而是由Doctrine来处理。我在我的User实体中添加了一个属性,该属性没有被持久化,但是它被表单简单地用于编辑用户的配置文件。在下面的代码中,这个属性是$profilePictureFile。随后生命周期回调确保在实体被持久化之前将该文件复制到相关位置(同样,在编辑和删除时也进行相应操作)。
尽管如此,我还是想在这里发布一个答案以帮助那些未来想要向FOSUserBundle用户添加个人资料图片的人。
相关文档是: http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/overriding_forms.rst
<?php
// DAWeldonExampleBundle/Entity/User.php
namespace DAWeldon\Example\UserBundle\Entity;

use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Security\Core\Util\SecureRandom;

/**
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks()
 * @ORM\Table(name="fos_user")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="datetime")
     */
    protected $lastEdited;

    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank(message="Please enter your surname.", groups={"Registration", "Profile"})
     */
    protected $surname;

    /**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank(message="Please enter your forename.", groups={"Registration", "Profile"})
     */
    protected $forename;

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

    /**
     * @Assert\File(maxSize="2048k")
     * @Assert\Image(mimeTypesMessage="Please upload a valid image.")
     */
    protected $profilePictureFile;

    // for temporary storage
    private $tempProfilePicturePath;

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

    public function __construct()
    {
        parent::__construct();
        // your own logic
    }

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

    /**
     * Set surname
     *
     * @param string $surname
     * @return User
     */
    public function setSurname($surname)
    {
        $this->surname = $surname;

        return $this;
    }

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

    /**
     * Set forename
     *
     * @param string $forename
     * @return User
     */
    public function setForename($forename)
    {
        $this->forename = $forename;

        return $this;
    }

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

    /**
     * Asks whether the user is granted a particular role
     * 
     * @return boolean
     */
    public function isGranted($role)
    {
        return in_array($role, $this->getRoles());
    }

    /**
     * Set nickname
     *
     * @param string $nickname
     * @return User
     */
    public function setNickname($nickname)
    {
        $this->nickname = $nickname;

        return $this;
    }

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

    /**
     * Get the best way to address this person
     * 
     * @return string
     */
    public function getBestAddress() {
        if (empty($this->getNickname()) and empty($this->getForename()) && empty($this->getSurname())) {
            return $this->getUsername();
        }
        elseif (empty($this->getNickname())) {
            return $this->getForename().' '.$this->getSurname();
        }
        else {
            return $this->getNickname();
        }
    }

    /**
     * Sets the file used for profile picture uploads
     * 
     * @param UploadedFile $file
     * @return object
     */
    public function setProfilePictureFile(UploadedFile $file = null) {
        // set the value of the holder
        $this->profilePictureFile       =   $file;
         // check if we have an old image path
        if (isset($this->profilePicturePath)) {
            // store the old name to delete after the update
            $this->tempProfilePicturePath = $this->profilePicturePath;
            $this->profilePicturePath = null;
        } else {
            $this->profilePicturePath = 'initial';
        }

        return $this;
    }

    /**
     * Get the file used for profile picture uploads
     * 
     * @return UploadedFile
     */
    public function getProfilePictureFile() {

        return $this->profilePictureFile;
    }

    /**
     * Set profilePicturePath
     *
     * @param string $profilePicturePath
     * @return User
     */
    public function setProfilePicturePath($profilePicturePath)
    {
        $this->profilePicturePath = $profilePicturePath;

        return $this;
    }

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

    /**
     * Get the absolute path of the profilePicturePath
     */
    public function getProfilePictureAbsolutePath() {
        return null === $this->profilePicturePath
            ? null
            : $this->getUploadRootDir().'/'.$this->profilePicturePath;
    }

    /**
     * Get root directory for file uploads
     * 
     * @return string
     */
    protected function getUploadRootDir($type='profilePicture') {
        // the absolute directory path where uploaded
        // documents should be saved
        return __DIR__.'/../../../../web/'.$this->getUploadDir($type);
    }

    /**
     * Specifies where in the /web directory profile pic uploads are stored
     * 
     * @return string
     */
    protected function getUploadDir($type='profilePicture') {
        // the type param is to change these methods at a later date for more file uploads
        // get rid of the __DIR__ so it doesn't screw up
        // when displaying uploaded doc/image in the view.
        return 'uploads/user/profilepics';
    }

    /**
     * Get the web path for the user
     * 
     * @return string
     */
    public function getWebProfilePicturePath() {

        return '/'.$this->getUploadDir().'/'.$this->getProfilePicturePath(); 
    }

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUploadProfilePicture() {
        if (null !== $this->getProfilePictureFile()) {
            // a file was uploaded
            // generate a unique filename
            $filename = $this->generateRandomProfilePictureFilename();
            $this->setProfilePicturePath($filename.'.'.$this->getProfilePictureFile()->guessExtension());
        }
    }

    /**
     * Generates a 32 char long random filename
     * 
     * @return string
     */
    public function generateRandomProfilePictureFilename() {
        $count                  =   0;
        do {
            $generator = new SecureRandom();
            $random = $generator->nextBytes(16);
            $randomString = bin2hex($random);
            $count++;
        }
        while(file_exists($this->getUploadRootDir().'/'.$randomString.'.'.$this->getProfilePictureFile()->guessExtension()) && $count < 50);

        return $randomString;
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     * 
     * Upload the profile picture
     * 
     * @return mixed
     */
    public function uploadProfilePicture() {
        // check there is a profile pic to upload
        if ($this->getProfilePictureFile() === null) {
            return;
        }
        // if there is an error when moving the file, an exception will
        // be automatically thrown by move(). This will properly prevent
        // the entity from being persisted to the database on error
        $this->getProfilePictureFile()->move($this->getUploadRootDir(), $this->getProfilePicturePath());

        // check if we have an old image
        if (isset($this->tempProfilePicturePath) && file_exists($this->getUploadRootDir().'/'.$this->tempProfilePicturePath)) {
            // delete the old image
            unlink($this->getUploadRootDir().'/'.$this->tempProfilePicturePath);
            // clear the temp image path
            $this->tempProfilePicturePath = null;
        }
        $this->profilePictureFile = null;
    }

     /**
     * @ORM\PostRemove()
     */
    public function removeProfilePictureFile()
    {
        if ($file = $this->getProfilePictureAbsolutePath() && file_exists($this->getProfilePictureAbsolutePath())) {
            unlink($file);
        }
    }



    /**
     * Set lastEdited
     *
     * @param \DateTime $lastEdited
     * @return User
     */
    public function setLastEdited($lastEdited)
    {
        $this->lastEdited = $lastEdited;

        return $this;
    }

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

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function setLastEditedValueAsNow() {
        $this->setLastEdited(new \DateTime());
    }

}

当然,个人资料表格看起来是这样的:
<?php
// DAWeldonExampleBundle/Form/Type/ProfileFormType.php
namespace DAWeldon\Example\UserBundle\Form\Type;

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

class ProfileFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // add your custom field
        $builder->add('username')
                ->add('surname')
                ->add('forename')
                ->add('nickname')
                ->add('profilePictureFile');
    }

    public function getParent()
    {
        return 'fos_user_profile';
    }

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

谢谢分享。我这里有一个问题。如果你想在个人资料编辑模板中显示用户的照片,你该如何做呢?如何在twig模板中调用User实体方法。干杯。 - cute.S
嘿可爱的S,我认为这是一个更与bundle继承相关的问题。你需要“覆盖”bundle,然后在bundle的命名空间内创建一个新模板,用于你的新表单和控制器。希望以下文档能帮到你!http://symfony.com/doc/current/cookbook/bundles/inheritance.html - Dom Weldon
我必须非常快地回复,如果你有问题,请回复! - Dom Weldon
1
值得注意的是,默认情况下,Sonata编辑个人资料或FOS用户资料编辑表单不会将enctype设置为“multipart/form-data”。如果没有这个设置,该解决方案将无法工作。 - Arkadiusz 'flies' Rzadkowolski
@DomWeldon 当我上传图片时,出现了“找不到文件”的错误。 - Muhammad Taqi
干得好,修复了。 - Alireza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接