Я пытаюсь создать форму подписки с Symfony4, и я думал, что она работает, но похоже, что когда я пытаюсь загрузить слишком большое изображение профиля, у меня возникает следующая ошибка: при сериализации была обнаружена циклическая ссылка объект класса «App \ Entity \ User» (настроенное ограничение: 1)

Однако я установил ограничение для своего свойства profilePicture относительно maxSize файла, который пользователь попытается загрузить, поэтому я не понимаю, почему это происходит (у меня все другие ошибки отображаются хорошо).

Вот часть кода, относящаяся к изображению профиля собственности:

/**
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank(message="Merci de bien vouloir sélectionner une image")
     * @Assert\Image(
     *     minRatio="1",
     *     maxRatio="1",
     *     minWidth="250",
     *     minHeight="250",
     *     minRatioMessage="Votre photo de profil doit avoir un ratio de 1:1",
     *     maxRatioMessage="Votre photo de profil doit avoir un ratio de 1:1",
     *     minWidthMessage="Votre image doit faire minimum {{ minWidth }} de large",
     *     maxWidthMessage="Votre image doit faire minimun {{ minHeight }} de hauteur", 
     *     maxSize="2M",
     *     maxSizeMessage="Votre image ne peut pas fait plus de 2M")
     */
    private $profilePicture;

Контроллер HomeController, работающий с формой подписки:

/**
     * @Route("/", name="home")
     */
    public function index(Request $request, UserPasswordEncoderInterface $passwordEncoder): Response
    {
        //To Manage registration
        $user = new User();
        $form = $this->createForm(RegistrationFormType::class, $user);
        $form->handleRequest($request);

        if ($form->isSubmitted() && !$form->isValid()) {
            return $this->json([
                "status" => "error(s)",
                "errors" => $form->getErrors(true, true)
            ], 200);
        }
        if ($form->isSubmitted() && $form->isValid()) {
            // move the file from the temp folder
            $fileUploader = new FileUploader($this->getParameter('profile_pictures_directory'));
            $profilePicture = $form['userProfile']['profilePicture']->getData();
            if ($profilePicture) {
                $profilePictureFilename = $fileUploader->upload($profilePicture);
                $user->getUserProfile()->setProfilePicture($profilePictureFilename);
            }
            // encode the plain password
            $user->setPassword(
                $passwordEncoder->encodePassword(
                    $user,
                    $form->get('plainPassword')->getData()
                )
            );
            $user->setCreationDate(new \DateTime());

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

            // do anything else you need here, like send an email

            return $this->json(["status" => "success"]);
        }

        return $this->render('home/index.html.twig', [
            'registrationForm' => $form->createView(),
        ]);
    }

Сервис FileUploader:

<?php
namespace App\Service;

use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class FileUploader
{
    private $targetDirectory;

    public function __construct($targetDirectory)
    {
        $this->targetDirectory = $targetDirectory;
    }

    public function upload(UploadedFile $file)
    {
        $originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
        $safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename);
        $fileName = $safeFilename.'-'.uniqid().'.'.$file->guessExtension();
        try {
            $file->move($this->getTargetDirectory(), $fileName);
        } catch (FileException $e) {

        }

        return $fileName;
    }

    public function getTargetDirectory()
    {
        return $this->targetDirectory;
    }
}

Между сущностью User и сущностью UserProfile существует связь OneToOne, в которой хранятся дополнительные данные о Пользователе.

Я бы хотел, чтобы это просто отображало сообщение об ошибке, касающееся размера файла, как это делается для всех других типов ошибок. Дайте мне знать, если вам понадобятся другие части моего кода.

0
Virginie P. 22 Окт 2019 в 10:56

1 ответ

Лучший ответ

Как оказалось, форма вызывает ошибки, и по крайней мере одна из ошибок имеет внутри сложный объект, который вводит рекурсию в процесс кодирования json *:

        return $this->json([
            "status" => "error(s)",
            "errors" => $form->getErrors(true, true)
        ], 200);

Чтобы этого не произошло, желательно подготовить отображаемые ошибки:

$formerrors = [];
foreach($form->getErrors(true, true) as $error) {
    $fieldname = ($origin = $error->getOrigin()) ? $origin->getName() : null;
    if($fieldname) {
        $formerrors[$fieldname] = $error->getMessage();
    } else {
        $formerrors[] = $error->getMessage();
}
return $this->json([
    "status" => "error(s)",
    "errors" => $formerrors,
], 200);

Поскольку ошибка может иметь не происхождение, кроме самой формы, условие в цикле может быть необходимым. также: для глубоких вложенных форм может потребоваться улучшить подход.

В качестве побочного примечания: возвращенный код состояния (200 в вашем случае) не в порядке, и он должен быть чем-то в пределах 400, поскольку запрос вызвал ошибку, ответ не должен быть 200 («ОК»).

*) для тех, кто задается вопросом, как это заметить: обычно требуется ВСЕ 30 секунд стандартного времени выполнения, пока не произойдет тайм-аут. и если стандартное время выполнения установлено на неограниченное, весь компьютер может зависнуть, потому что он медленно (или очень быстро) съедает всю память, чтобы подготовить бесконечно длинный ответ. это почти всегда происходит, когда var_dump или print_r сложные объекты, которые в своих ссылках (возможно, многоуровневые) ссылаются на сложные объекты или друг на друга, создавая бесконечный цикл, поскольку процесс сериализации следует ссылкам глубже в объекты. dump, а также dd (вывод отладки symfony) обычно работают способом лучше (избегается рекурсия) и, таким образом, позволяют проверять объект. - однако и dd, и dump не должны использоваться в производстве, также компонент отладки по умолчанию отключен в prod env.

1
Jakumi 24 Окт 2019 в 05:58