不允许序列化'SymfonyComponentHttpFoundationFileFile',Symfony4



我已经在我的User类中添加了一个头像图像。当我想呈现我的编辑表单时,我收到此错误

不允许序列化"Symfony\Component\HttpFoundation\File\File">

我试图通过根据Symfony官方文档在我的User类中实现Serializable来解决这个问题。但是当我实现它时,它重定向到登录页面,Authentication转向anon.,通过再次登录,它再次重定向到登录页面并保持anon.

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

这是我的UserEntity,用户.php:

<?php
namespace AppEntity;
use DoctrineORMMapping as ORM;
use SymfonyBridgeDoctrineValidatorConstraintsUniqueEntity;
use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentValidatorConstraints as Assert;

/**
* @ORMEntity(repositoryClass="AppRepositoryUserRepository")
* @ORMTable(name="user")
* @UniqueEntity(fields={"username"}, message="This username has been taken!")
*/
class User implements UserInterface
{
/**
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
* @ORMColumn(type="integer")
*/
private $id;
/**
* @ORMColumn(type="string", unique=true,length=191)
* @AssertNotBlank()
* @AssertLength(min="5", minMessage="Username most contain at least 5 characters!")
*/
private $username;
/**
* @ORMColumn(type="string")
*/
private $password;
/**
* @ORMColumn(type="string")
*/
private $displayName;
/**
* @ORMColumn(type="boolean")
*/
private $showAdminBar;
/**
* @ORMOneToMany(targetEntity="Post", mappedBy="owner")
*/
private $posts;
/**
* @ORMColumn(type="string")
*/
private $avatar;
/**
* @AssertNotBlank(groups={"Registration"})
* @AssertLength(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;
}
}

这是我的用户控制器.php

<?php
namespace AppControllerAdmin;
use AppConstants;
use AppEntityUser;
use AppFormUserType;
use SensioBundleFrameworkExtraBundleConfigurationSecurity;
use SymfonyComponentHttpFoundationFileFile;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentRoutingAnnotationRoute;
use SymfonyBundleFrameworkBundleControllerController;

/**
* @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,用户类型.php

<?php
namespace AppForm;
use AppEntityUser;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormExtensionCoreTypeFileType;
use SymfonyComponentFormExtensionCoreTypePasswordType;
use SymfonyComponentFormExtensionCoreTypeRepeatedType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
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,
]);
}
}

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

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

为了解决这个问题,首先我尝试从数据库中获取单独的User对象,但不幸的是,Doctrine再次给了我User对象的确切参考。(这不是错误。感谢教义。查询得越少越好太聪明了)。

其次,在将User对象发送到UserType窗体之前,我自己在控制器中clone对象,然后一切顺利。

但这不是最佳实践,因为您可能还有其他一些 注册、个人资料更新或其他情况出现问题 可能有User类。

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

您的应用程序中可能有一些其他设计,但正如我所经历的,不要将File字段直接分配给正在实现UserInterfaceUser实体!

我做了这样的事情:

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

而且效果很好。

很好的答案 Sohell,你总结得很好。

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

它允许用户对象更新并在会话中保留正确的对象,排除 File 对象

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

例:

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

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

1. 第一个错误

不允许序列化"Symfony\Component\HttpFoundation\File\File">

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

您也不能在输入功能时设置profileAction()头像字段......这会递归更改名称,在名称前面添加前缀目录。

2.安全错误,返回登录页面,匿名身份验证

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

希望这对你有帮助。

在"用户"实体中:

/**
* @ORMOneToMany(targetEntity="UserFile",mappedBy="user", cascade={"remove"})
* @ORMOrderBy({"created_at" = "DESC"})
*/
protected $userfiles;

用户文件类:

<?php

namespace AppEntity;
use AppRepositoryUserFileRepository;
use DoctrineCommonCollectionsArrayCollection;
use DoctrineORMMapping as ORM;
use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentSecurityCoreValidatorConstraints as SecurityAssert;
use SymfonyComponentValidatorConstraints as Assert;
use SymfonyComponentHttpFoundationFileFile;
use SymfonyComponentHttpFoundationFileUploadedFile;
use VichUploaderBundleEntityFile as EmbeddedFile;
use VichUploaderBundleMappingAnnotation as Vich;
/**
* @ORMEntity(repositoryClass=UserFileRepository::class)
* @VichUploadable
* @ORMHasLifecycleCallbacks
*/
class UserFile implements Serializable
{
/**
* Constructor
*/
public function __construct()
{
// voor de VichUploader
$this->image = new EmbeddedFile();
}
/**
* @ORMId
* @ORMGeneratedValue
* @ORMColumn(type="integer")
*/
private $id;
/**
* @ORMManyToOne(targetEntity="User", inversedBy="userfiles")
* @ORMJoinColumn(name="user_id", referencedColumnName="id")
* @ORMOrderBy({"achternaam" = "DESC"})
* @AssertNotBlank()
*/
protected $user;
/**
* @var string
* @ORMColumn(name="type", type="string", length=20, nullable=true)
*/
protected $type;
//--------------------------------------------------------------------------------
/**
* @ORMColumn(name="created_at", type="datetime", nullable=true)
*
*/
protected $created_at;
/**
* @ORMColumn(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.
*
* @VichUploadableField(mapping="gebruiker_foto",
*     fileNameProperty="image.name",
*     size="image.size",
*     mimeType="image.mimeType",
*     originalName="image.originalName",
*     dimensions="image.dimensions")
*
* @var File|null
*/
private $imageFile;
/**
* @ORMEmbedded(class="VichUploaderBundleEntityFile")
* @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
//--------------------------------------------------------------------------------
/**
* @ORMPrePersist()
* Hook on pre-persist operations
*/
public function prePersist()
{
$this->created_at = new DateTime;
$this->updated_at = new DateTime;
}
/**
* @ORMPreUpdate()
* Hook on pre-update operations
*/
public function preUpdate()
{
$this->updated_at = new DateTime;
}
/**
* @ORMPostPersist()
* @ORMPostUpdate()
*/
public function verkleinFoto()
{
if (null === $this->imageFile) {
return;
}
// create smaller image, wel graag vierkante plaatjes!!
$width    = 160;
$imagine  = new ImagineGdImagine();
$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);
}
}

最近又活跃于symfony,通过symfony 5工作。

但我所做的是:

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

如果这样做,您可以在进行身份验证时添加文件/照片,并且不会收到该错误

最新更新