不允许序列化'Symfony \ Component \ HttpFoundation \ File \ File',Symfony4

时间:2018-04-11 18:31:06

标签: php serialization image-uploading symfony4

我在我的User课程中添加了头像图片。当我想渲染我的编辑表单时,我收到了这个错误

  

不允许序列化'Symfony \ Component \ HttpFoundation \ File \ File'

我尝试根据Symfony Official Documentation在我的\Serializable课程中实施User来解决问题。但是当我实现它时,它重定向到登录页面并且Authentication转向anon.并再次登录,它再次重定向到登录页面并保持anon.

  

我应该提一下,我已经设置了一些授权。如果您是“匿名”,它会将您重定向到登录页面。并希望访问一些受保护的路线。

这是我的UserEntity,User.php:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;


/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @ORM\Table(name="user")
 * @UniqueEntity(fields={"username"}, message="This username has been taken!")
 */
class User implements UserInterface
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", unique=true,length=191)
     * @Assert\NotBlank()
     * @Assert\Length(min="5", minMessage="Username most contain at least 5 characters!")
     */
    private $username;

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

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

    /**
     * @ORM\Column(type="boolean")
     */
    private $showAdminBar;

    /**
     * @ORM\OneToMany(targetEntity="Post", mappedBy="owner")
     */
    private $posts;

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

    /**
     * @Assert\NotBlank(groups={"Registration"})
     * @Assert\Length(min="6", minMessage="Password most contain at least 6 characters!")
     */
    private $plainPassword;

    public function getUsername()
    {
        return $this->username;
    }

    public function getRoles()
    {
        return ['ROLE_ADMIN'];
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function getSalt()
    {
    }

    public function eraseCredentials()
    {
        $this->plainPassword = null;
    }

    public function serialize()
    {
        return serialize(array(
            $this->id,
            $this->username,
            $this->displayName,
            $this->avatar,
            // see section on salt below
            // $this->salt,
        ));
    }

    /**
     * @param mixed $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

    /**
     * @param mixed $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

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

    /**
     * @param mixed $plainPassword
     */
    public function setPlainPassword($plainPassword)
    {
        $this->plainPassword = $plainPassword;
        //To make sure that Doctrine see the entity as "dirty"
        $this->password = null;
    }

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

    /**
     * @param mixed $displayName
     */
    public function setDisplayName($displayName)
    {
        $this->displayName = $displayName;
    }

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

    /**
     * @param mixed $showAdminBar
     */
    public function setShowAdminBar($showAdminBar)
    {
        $this->showAdminBar = $showAdminBar;
    }

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

    /**
     * @param mixed $posts
     */
    public function setPosts($posts)
    {
        $this->posts = $posts;
    }

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

    /**
     * @param mixed $avatar
     */
    public function setAvatar($avatar)
    {
        $this->avatar = $avatar;
    }

    /**
     * @param mixed $id
     */
    public function setId($id)
    {
        $this->id = $id;
    }
}

这是我的UserController.php

<?php

namespace App\Controller\Admin;

use App\Constants;
use App\Entity\User;
use App\Form\UserType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;


/**
 * @Route("/admin/user")
 * @Security("is_granted('ROLE_ADMIN')")
 */
class UserController extends Controller
{
    /**
     * @Route("/profile", name="admin_user_profile")
     */
    public function profileAction(Request $request)
    {
        $user = $this->getUser();

        $user->setAvatar(
            new File(Constants::UPLOAD_AVATAR.'/'.$user->getAvatar())
        );


        $form = $this->createForm(UserType::class, $user);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $user = $form->getData();


            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();

            $this->addFlash('success', 'Your Info Has Been Updated!');

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


        return $this->render('admin/user/profile.html.twig', [
            'user' => $user,
            'form' => $form->createView()
        ]);
    }

    /**
     * @Route("/list", name="admin_user_list")
     */
    public function listAction(Request $request)
    {
        $em = $this->getDoctrine()->getManager();
        $users = $em->getRepository(User::class)
            ->findAll();

        return $this->renderView('admin/user/list.html,twig',[
            'users' => $users
        ]);
    }
}

这是我的UserForm,UserType.php

<?php

namespace App\Form;

use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('displayName')
            ->add('plainPassword', RepeatedType::class, [
                'type' => PasswordType::class
            ])
            ->add('avatar',FileType::class)
        ;
    }

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

8 个答案:

答案 0 :(得分:2)

经过一些调试后,我自己找到了解决方案。

问题是,当User实体正在实施UserInterface时,用户提供者(实际上是幕后的Doctrine)尝试序列化User对象以将其存储在会话,但由于我将它分配给这个课程的文件,它失败了它的职业生涯!

要解决这个问题,首先我尝试从数据库中获取单独的User对象,但不幸的是,Doctrine再次给了我User对象的确切参考。(这不是一个bug。感谢Doctrine。它太聪明了,无法尽可能少地查询。)

其次,我clone {@ 1}}我自己在控制器中将对象发送到User表单,然后一切顺利。

  

但这不是最佳做法,因为你可能还有其他一些   注册,配置文件更新或您的其他方案的问题   可能有UserType类。

在我的应用程序中,我添加了另一个名为User的实体,它将文件与文件系统一起存储,每个实体如Media需要一些媒体(如此处的用户头像),只需要{ {1}}与此实体的关系。在这种情况下,您只需将名称文件保存为User类中的头像字段中的ManyToOne

您的应用程序中可能有其他一些设计,但正如我所经历的那样,不要将string字段直接分配给正在实施User的{​​{1}}实体!

答案 1 :(得分:0)

1.第一个错误

  

序列化&#39; Symfony \ Component \ HttpFoundation \ File \ File&#39;不允许

  • 这是因为profileAction()中的这条指令:您将File对象分配给了头像字段...这变得不可序列化。
    public function profileAction(Request $request) 
     { 
         $user = $this->getUser();       
         $user->setAvatar( 
           new File(Constants::UPLOAD_AVATAR.'/'.$user->getAvatar())
         );
        ...
    }

您无法在输入profileAction()函数时随时设置头像字段......这会递归更改名称,在名称前面添加前缀目录。

2.安全错误,返回登录页面,匿名。认证

如果您正确删除了序列化问题,则用户身份验证可以正常进行身份验证状态...如果您再次遇到问题,请使用匿名登录表单循环。 status,您可能必须按照SF4中的要求实现EquatableInterface。

希望这对你有所帮助。

答案 2 :(得分:0)

很好的回答Sohell,您总结得很完美。

我遇到了同样的问题,并找到了解决方法,在刷新了entityManager之后,我只是将用户对象的imageFile属性设置为null。

让我们更新用户对象并在会话中保留正确的对象,排除文件对象

答案 3 :(得分:0)

小心!如果您在表单中有一些错误,symfony仍将尝试在表单页面渲染中序列化您上载的文件。如果$form->isValid()也返回false,我必须将图像文件设置为null。

答案 4 :(得分:0)

我已经做了类似的事情:

class User implements UserInterface, Serializable {

    // our image
    private $profileImage;

    /*
    Rest of our awesome entity
    */

    public function serialize()
    {
        $this->profileImage = base64_encode($this->profileImage);
    }

    public function unserialize($serialized)
    {
        $this->profileImage = base64_decode($this->profileImage);

    }
}

运行良好。

答案 5 :(得分:0)

最近再次活跃于 symfony,在 symfony 5 中工作。

但我所做的是:

  1. 创建了实现用户界面的用户实体
  2. 添加实体 Userfile 并在 User 实体中建立多对一关系
  3. 使该类可序列化(这就是诀窍)

如果这样做,您可以在通过身份验证时添加文件/照片,并且不会出现该错误

答案 6 :(得分:0)

在用户实体中:

/**
 * @ORM\OneToMany(targetEntity="UserFile",mappedBy="user", cascade={"remove"})
 * @ORM\OrderBy({"created_at" = "DESC"})
 */
protected $userfiles;

用户文件类:

<?php


namespace App\Entity;

use App\Repository\UserFileRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Validator\Constraints as SecurityAssert;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Vich\UploaderBundle\Entity\File as EmbeddedFile;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * @ORM\Entity(repositoryClass=UserFileRepository::class)
 * @Vich\Uploadable
 * @ORM\HasLifecycleCallbacks
 */
class UserFile implements \Serializable
{
    /**
     * Constructor
     */
    public function __construct()
    {
        // voor de VichUploader
        $this->image = new EmbeddedFile();
    }

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="User", inversedBy="userfiles")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     * @ORM\OrderBy({"achternaam" = "DESC"})
     * @Assert\NotBlank()
     */
    protected $user;

    /**
     * @var string
     * @ORM\Column(name="type", type="string", length=20, nullable=true)
     */
    protected $type;

    //--------------------------------------------------------------------------------

    /**
     * @ORM\Column(name="created_at", type="datetime", nullable=true)
     *
     */
    protected $created_at;

    /**
     * @ORM\Column(name="updated_at", type="datetime", nullable=true)
     *
     */
    protected $updated_at;

    //--------------------------------------------------------------------------------
    // de kolommen voor het uploaden van een image via VichUploader
    //--------------------------------------------------------------------------------

    /**
     * NOTE: This is not a mapped field of entity metadata, just a simple property.
     *
     * @Vich\UploadableField(mapping="gebruiker_foto",
     *     fileNameProperty="image.name",
     *     size="image.size",
     *     mimeType="image.mimeType",
     *     originalName="image.originalName",
     *     dimensions="image.dimensions")
     *
     * @var File|null
     */
    private $imageFile;

    /**
     * @ORM\Embedded(class="Vich\UploaderBundle\Entity\File")
     * @var EmbeddedFile
     */
    private $image;


    //--------------------------------------------------------------------------------

    public function getId(): ?int
    {
        return $this->id;
    }

    //--------------------------------------------------------------------------------

    public function getUser() : ?User
    {
        return $this->user;
    }

    public function setUser(?User $user)
    {
        $this->user = $user;
        return $this;
    }

    /**
     * @return string
     */
    public function getType(): ?string
    {
        return $this->type;
    }

    /**
     * @param string $type
     * @return UserFile
     */
    public function setType(?string $type): self
    {
        $this->type = $type;
        return $this;
    }

    //--------------------------------------------------------------------------------

    /**
     *
     */
    public function getCreatedAt()
    {
        return $this->created_at;
    }

    /**
     *
     */
    public function getUpdatedAt()
    {
        return $this->updated_at;
    }

    //--------------------------------------------------------------------------------
    // callbacks
    //--------------------------------------------------------------------------------

    /**
     * @ORM\PrePersist()
     * Hook on pre-persist operations
     */
    public function prePersist()
    {
        $this->created_at = new \DateTime;
        $this->updated_at = new \DateTime;
    }

    /**
     * @ORM\PreUpdate()
     * Hook on pre-update operations
     */
    public function preUpdate()
    {
        $this->updated_at = new \DateTime;
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function verkleinFoto()
    {
        if (null === $this->imageFile) {
            return;
        }

        // create smaller image, wel graag vierkante plaatjes!!
        $width    = 160;
        $imagine  = new \Imagine\Gd\Imagine();
        $image    = $imagine->open($this->imageFile);
        $size     = $image->getSize();

        $image->resize($size->widen($width));
        $realpath = $this->imageFile->getRealPath();
        $image->save($realpath);

        // 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->file->move($this->getUploadRootDir(), $this->path);

        //unset($this->file);
    }

    //--------------------------------------------------------------------------------
    // de setters/getters voor het uploaden van een image via VichUploader
    //--------------------------------------------------------------------------------

    /**
     * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
     * of 'UploadedFile' is injected into this setter to trigger the  update. If this
     * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
     * must be able to accept an instance of 'File' as the bundle will inject one here
     * during Doctrine hydration.
     *
     * @param File|UploadedFile|null $imageFile
     */
    public function setImageFile(?File $imageFile = null): void
    {
        $this->imageFile = $imageFile;

        if (null !== $imageFile) {
            // It is required that at least one field changes if you are using doctrine
            // otherwise the event listeners won't be called and the file is lost
            $this->updated_at = new \DateTimeImmutable();
        }
    }

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

    public function setImage(EmbeddedFile $image): void
    {
        $this->image = $image;
    }

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

    //--------------------------------------------------------------------------------
    public function serialize()
    {
        $this->imageFile = base64_encode($this->imageFile);
    }

    public function unserialize($serialized)
    {
        $this->imageFile = base64_decode($this->imageFile);

    }

}

答案 7 :(得分:0)

这可能对某人有所帮助,这个问题实际上没什么大不了的。只需在实体的 @Ignore 字段上添加 File 注释 - 为我解决了该问题。

示例:

/**
 * @Vich\UploadableField(mapping="users", fileNameProperty="imageName")
 * @Ignore()
 */
 private ?File $imageFile = null;