[ОБНОВЛЕНО]: 2019/06/24 - 23;28
Загружая файл с формой, я сталкиваюсь со следующей ошибкой:
This value should be of type string
Конструктор форм настроен на FileType, как и должно быть:
FormType
class DocumentType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
/** @var Document $salle */
$document=$options['data']; //Unused for now
$dataRoute=$options['data_route']; //Unused for now
$builder->add('nom')
->add('description')
->add('fichier', FileType::class, array(
//'data_class' is not the problem, tested without it.
//see comments if you don't know what it does.
'data_class'=>null,
'required'=>true,
))
->add('isActif', null, array('required'=>false));
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'data_class'=>Document::class,
'data_route'=>null,
]);
}
}
И у моего геттера и сеттера нет подсказки типа, чтобы убедиться, что UploadedFile::__toString() не будет вызываться:
Entity
class Document {
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type = "integer")
*/
private $id;
/**
* @ORM\Column(type = "string", length=100)
*/
private $nom;
/**
* @ORM\Column(type = "string", length=40)
*/
private $fichier;
/**
* @ORM\Column(type = "boolean")
*/
private $isActif;
/**
* @ORM\ManyToOne(targetEntity = "App\Entity\Salle", inversedBy = "documents")
* @ORM\JoinColumn(onDelete = "CASCADE")
*/
private $salle;
/**
* @ORM\ManyToOne(targetEntity = "App\Entity\Stand", inversedBy = "documents")
* @ORM\JoinColumn(onDelete = "CASCADE")
*/
private $stand;
public function __construct() {
$this->isActif=true;
}
public function __toString() {
return $this->getNom();
}
public function getId(): ?int {
return $this->id;
}
public function getNom(): ?string {
return $this->nom;
}
public function setNom(string $nom): self {
$this->nom=$nom;
return $this;
}
public function getFichier()/*Removed type hint*/ {
return $this->fichier;
}
public function setFichier(/*Removed type hint*/$fichier): self {
$this->fichier=$fichier;
return $this;
}
public function getIsActif (): ?bool {
return $this->isActif;
}
public function setIsActif (bool $isActif): self {
$this->isActif=$isActif;
return $this;
}
public function getSalle(): ?Salle {
return $this->salle;
}
public function setSalle(?Salle $salle): self {
$this->salle=$salle;
return $this;
}
public function getStand(): ?Stand {
return $this->stand;
}
public function setStand(?Stand $stand): self {
$this->stand=$stand;
return $this;
}
}
Тем не менее, валидатор формы по-прежнему ожидает объект string, а не объект UploadedFile.
Controller
/**
* @Route("/dashboard/documents/new", name = "document_new", methods = {"POST"})
* @Route("/dashboard/hall-{id}/documents/new", name = "hall_document_new", methods = {"POST"})
* @Route("/dashboard/stand-{id}/documents/new", name = "stand_document_new", methods = {"POST"})
* @param Router $router
* @param Request $request
* @param FileUploader $fileUploader
* @param SalleRepository $salleRepository
* @param Salle|null $salle
* @param Stand|null $stand
* @return JsonResponse
* @throws Exception
*/
public function new(Router $router, Request $request, FileUploader $fileUploader, SalleRepository $salleRepository, Salle $salle=null, Stand $stand=null) {
if ($this->isGranted('ROLE_ORGANISATEUR')) {
$route=$router->match($request->getPathInfo())['_route'];
if (($route == 'hall_document_new' && !$salle) || ($route == 'stand_document_new' && !$stand)) {
//ToDo [SP] set message
return $this->json(array(
'messageInfo'=>array(
array(
'message'=>'',
'type'=>'error',
'length'=>'',
)
)
));
}
$document=new Document();
if ($route == 'hall_document_new') {
$action=$this->generateUrl($route, array('id'=>$salle->getId()));
} elseif ($route == 'stand_document_new') {
$action=$this->generateUrl($route, array('id'=>$stand->getId()));
} else {
$action=$this->generateUrl($route);
}
$form=$this->createForm(DocumentType::class, $document, array(
'action'=>$action,
'method'=>'POST',
'data_route'=>$route,
));
$form->handleRequest($request);
if ($form->isSubmitted()) {
//Fail here, excepting a string value (shouldn't), got UploadedFile object
if ($form->isValid()) {
if ($route == 'hall_document_new') {
$document->setSalle($salle);
} elseif ($route == 'stand_document_new') {
$document->setStand($stand);
} else {
$accueil=$salleRepository->findOneBy(array('isAccueil'=>true));
if ($accueil) {
$document->setSalle($accueil);
} else {
//ToDo [SP] set message
return $this->json(array(
'messageInfo'=>array(
array(
'message'=>'',
'type'=>'',
'length'=>'',
)
)
));
}
}
/** @noinspection PhpParamsInspection */
$filename=$fileUploader->uploadDocument($document->getFichier());
if ($filename) {
$document->setFichier($filename);
} else {
//ToDo [SP] set message
return $this->json(array(
'messageInfo'=>array(
array(
'message'=>'',
'type'=>'error',
'length'=>'',
)
)
));
}
$entityManager=$this->getDoctrine()->getManager();
$entityManager->persist($document);
$entityManager->flush();
return $this->json(array(
'modal'=>array(
'action'=>'unload',
'modal'=>'mdcDialog',
'content'=>null,
)
));
} else {
//ToDo [SP] Hide error message
return $this->json($form->getErrors(true, true));
// return $this->json(false);
}
}
return $this->json(array(
'modal'=>array(
'action'=>'load',
'modal'=>'mdcDialog',
'content'=>$this->renderView('salon/dashboard/document/new.html.twig', array(
'salle'=>$salle,
'stand'=>$stand,
'document'=>$document,
'form'=>$form->createView(),
)),
)
));
} else {
return $this->json(false);
}
}
services.yaml
parameters:
locale: 'en'
app_locales: en|fr
ul_document_path: '%kernel.root_dir%/../public/upload/document/'
services:
_defaults:
autowire: true
autoconfigure: true
bind:
$locales: '%app_locales%'
$defaultLocale: '%locale%'
$router: '@router'
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
App\Listener\kernelListener:
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
- { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
App\Service\FileUploader:
arguments:
$ulDocumentPath: '%ul_document_path%'
Пожалуйста, покажите нам контроллер, который обрабатывает форму и параметры из config/services.yaml, спасибо.
обновлен @ArleighHix
И App\Service\FileUploader тоже пожалуйста
@ArleighHix ничего особенного, кроме функции для создания имени файла и функции для перемещения файла в нужную папку. Не требуется для этого вопроса, ошибка возникает еще до вызова одной из функций в службе FileUploader.
почему эта строка 'data_class'=>null в типе формы?
@ArleighHix При загрузке формы для редактирования уже сохраненного элемента форма по-прежнему ожидает экземпляр File. Вы, конечно, можете восстановить файл с помощью new File(), но это не обязательно. Установка data_class на null сделает так, что вам не нужно регенерировать файл при редактировании.




В config/packages/validator.yaml закомментируйте эти строки, если они существуют:
framework:
validation:
# Enables validator auto-mapping support.
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
#auto_mapping:
# App\Entity\: []
См. выпуск Symfony 4.3 [Проверка] Активировать автоматическую проверку с помощью аннотации #32070..
Исправил это для меня :)
Это редактируется, моя проблема заключается в создании. И Assert то, что есть... Подробнее о Assert, это не соответствует моим потребностям. Кроме того, я не ограничен одним типом файла. Наконец, вы можете увидеть 'data_class'=>null, в моем formType. Это сделано для того, чтобы избежать проблемы воссоздания файлового объекта при редактировании.
Чтобы уточнить, вот моя проблема, хотя я установил FileType в своем конструкторе форм, он все еще исключает string, чего не должно быть.
Я удалил аннотации Assert из своего кода, и он все еще работает. Я думал, что изначально добавил Assert\Type(type = "File" после получения исключения. Извините, я не знаю, как это работает. Я не знаю, почему аннотации, по-видимому, исправили проблему для @Martijn.
Странный. Моим единственным изменением было добавление активов. После того, как вы очистите кеш, он все еще работает?
@Martijn Я очистил кеш с помощью php bin/console cache:clear и обновил браузер, и загрузка файлов по-прежнему работает без аннотаций Assert. Я использую Symfony 4.3.1 и PHP v7.2.19-0ubuntu0.18.04.1. Какие версии вы используете? Не могли бы вы попробовать удалить Asserts из своей сущности и проверить, повторяется ли исключение?
Я не получаю «должна быть строка», когда удаляю ее, все работает, но ничего не делает :) S4.3.1, php7.2.4, windows10 через WAMP
@Preciel Несмотря на то, что это не кажется необходимым и не соответствует вашим потребностям, не могли бы вы добавить аннотации Assert, как описано в моем ответе выше? Исключение все еще происходит? Если нет, попробуйте удалить эти аннотации Assert и посмотрите, произойдет ли исключение?
Пробовал, проблема не меняется, ошибка все равно есть. Каким-то образом, чтобы быть исключенным... Assert (Constraints) предназначен для утверждения, проверки, проверки и т. д.... Не для преобразования. Не знаю, как это решило проблему @Martijn...
@Preciel Содержит ли ваш config/packages/validator.yaml строки auto_mapping, как в моем отредактированном ответе? Мне удалось воспроизвести ваше исключение This value should be of type string, добавив эти строки в мой validator.yaml и удалив аннотации Assert на $fichier.
Хорошая находка. Удалил валидатор auto_mapping и теперь он работает. Вы должны очистить свой ответ, чтобы оставить только часть о auto_mapping (и ссылки на источники). Я думаю, вы неправильно понимаете использование Assert... ;)
@Preciel Очищенный ответ в соответствии с вашим направлением.
В конструкторе форм вы устанавливаете data_class на null:
->add('fichier', FileType::class, array(
'data_class'=>null,
'required'=>true,
))
Но FileType на самом деле ожидает, что какой-то класс данных будет определен внутри. У него есть некоторая логика для динамического определения класса: это либо Symfony\Component\HttpFoundation\File\File для загрузки одного файла, либо null для нескольких файлов.
Таким образом, вы фактически заставляете свой файловый контроль быть многофайловым, но тип целевого поля — string. Symfony угадывает тип и соответственно выбирает элементы управления (например, логическое поле сущности будет представлено флажком) — если вы не укажете явный тип элемента управления и параметры.
Итак, я думаю, вы должны удалить data_class из своих вариантов, и это решит проблему.
Вот ссылка на конкретное место, чтобы оно вело себя так, как я описал: https://github.com/symfony/form/blob/master/Extension/Core/Type/FileType.php#L114
Как видите, он определяет значение data_class и некоторые другие значения, а затем принимает решение setDefaults(), т. е. эти правильные значения есть, если только вы их не переопределите. Немного хрупкая архитектура, я бы сказал, но это то, с чем мы должны работать.
Я сделал минимальный проект, чтобы воспроизвести ваши проблемы, но он просто работает (на самом деле, data_class не влияет на результаты, что делает мой ответ неверным, а required также не имеет значения). Но опять же, это просто работает. У меня только две подсказки банкомата: 1) data_route -- что это такое? ваше расширение формы? какой-то стандартный компонент? стандартный FormType не имеет такой опции; 2) action -- вы уверены, что он указывает именно на этот контроллер? В общем, я рад поделиться своим примером проекта, хотя он будет очень похож на symfony.com/doc/current/controller/upload_file.html с вашими именами классов.
data_route — это просто строка, я использую ее, чтобы узнать, откуда я пришел, чтобы я мог настроить форму (у меня не будет одинаковых полей при редактировании). action правильно, он использует один из 3 маршрутов, доступных для этого контроллера (я обновил свой код)
Хорошо, тогда это не так. Опять же, мой пример проекта работает нормально, и его код похож на ваш. Еще несколько вопросов: 1) какую версию symfony вы используете? 2) есть ли в ваших проектах какие-либо пользовательские классы расширения формы? 3) какие-нибудь преобразователи моделей или преобразователи данных в вашем DocumentType? 4) какие-либо специальные параметры в формах или полях форм, например inherit_data, mapped и т. д.?
1) Symfony 4 (с тегами), 2) нет, 3) нет, 4) нет
Можете ли вы указать точную версию Symfony, например вывод из `composer show | grep symfony/framework` (для меня это будет 4.3.1). Понижение до чего-то вроде 4.0 было бы адской задачей...
Я также использую Symfony 4.3.1, обновленную прошлой ночью, хотя это может быть проблема с самой Symfony.
Итак, я воспроизвел ваш пример как можно ближе (учитывая весь код, который я вижу в вашем вопросе), и он отлично работает. Видимо проблема в частях, которые я не вижу? Других идей пока нет, извините.
Я просто "откатюсь" и попробую еще раз, может что-то упустил. Нечего особо не показать, сам проект достаточно свежий, сделано не много. Может просто не повезло.
Если он не большой/чувствительный, не могли бы вы как-нибудь поделиться полным кодом?
Обновил мой вопрос. Дайте мне знать, если вам нужно что-то еще.
Я сделал еще один тест с нуля, не генерировал объекты и т. д., но вставил ваш код (закомментировал некоторые несвязанные вещи, такие как SalleRepository), но он работает нормально. (Ваш код, кажется, из разных версий вашего приложения, поскольку DocumentType относится к полям, которых нет в документе).
Единственная заметная разница, о которой я мог подумать, заключается в следующем: это действие контроллера принимает POST, но я не вижу, как создается ваша начальная форма. TBH Я не понимаю, как это может вызвать проблемы, но технически мы проверяли только приемную часть. Возможно, это даст вам некоторые подсказки.
Еще одна возможная подсказка: строка «Это значение должно быть типа» существует только в symfony/validator источниках. Итак, если вы не добавили именно эту строку где-то в свой код, это происходит из-за некоторого ограничения. Может быть, искать использование пространства имен «Ограничение» в вашем коде?
Извините за несоответствие формы, просто я убрал неактуальные параметры. Насчет моей формы, она основная form_start, form_widget, form_end. Я еще не редактировал эту часть. И единственное «Ограничение», которое у меня есть, находится в пользовательском объекте (уникальный адрес электронной почты). Я вообще в растерянности... :/
Дайте мне знать, если вы решите поделиться своим проектом (публично или лично со мной).
У меня сейчас точно такая же проблема, код похож на OP