Я использую почтальон для проверки json-api. У меня есть связанные объекты:
Публикация:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass = "App\Repository\PublicationRepository")
*/
class Publication
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type = "integer")
*/
private $id;
/**
* @Assert\NotBlank
* @ORM\Column(type = "string", length=100)
*/
private $title;
/**
* @Assert\NotBlank
* @ORM\Column(type = "string", length=500)
*/
private $body;
/**
* @ORM\OneToMany(targetEntity = "App\Entity\Comment", mappedBy = "publication", orphanRemoval=true)
*/
private $comments;
public function __construct()
{
$this->comments = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getBody(): ?string
{
return $this->body;
}
public function setBody(string $body): self
{
$this->body = $body;
return $this;
}
/**
* @return Collection|Comment[]
*/
public function getComments(): Collection
{
return $this->comments;
}
public function addComment(Comment $comment): self
{
if (!$this->comments->contains($comment)) {
$this->comments[] = $comment;
$comment->setPublication($this);
}
return $this;
}
public function removeComment(Comment $comment): self
{
if ($this->comments->contains($comment)) {
$this->comments->removeElement($comment);
// set the owning side to null (unless already changed)
if ($comment->getPublication() === $this) {
$comment->setPublication(null);
}
}
return $this;
}
}
Комментарий:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass = "App\Repository\CommentRepository")
*
*/
class Comment
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type = "integer")
*/
private $id;
/**
* @Assert\NotBlank
* @ORM\Column(type = "string", length=255)
*/
private $body;
/**
* @Assert\Positive
* @ORM\Column(type = "integer")
*/
private $likeCount;
/**
* @return mixed
*/
public function getLikeCount()
{
return $this->likeCount;
}
/**
* @param mixed $likeCount
*/
public function setLikeCount($likeCount): void
{
$this->likeCount = $likeCount;
}
/**
* @ORM\ManyToOne(targetEntity = "App\Entity\Publication", inversedBy = "comments")
* @ORM\JoinColumn(nullable=false)
*/
private $publication;
public function getId(): ?int
{
return $this->id;
}
public function getBody(): ?string
{
return $this->body;
}
public function setBody(string $body): self
{
$this->body = $body;
return $this;
}
public function getPublication(): ?Publication
{
return $this->publication;
}
public function setPublication(?Publication $publication): self
{
$this->publication = $publication;
return $this;
}
}
И классы формы:
Тип публикации:
<?php
namespace App\Form;
use App\Entity\Publication;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PublicationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('body')
->add('comments', CollectionType::class, [
'entry_type' => CommentType::class,
'entry_options' => ['label' => false],
]);
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Publication::class,
'csrf_protection'=> false
]);
}
}
Тип комментария:
<?php
namespace App\Form;
use App\Entity\Comment;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CommentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('body')
->add('publication')
->add('likeCount');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Comment::class,
'csrf_protection'=> false
]);
}
}
В соответствии с этим: https://symfony.com/doc/current/form/form_collections.html форма публикации может получать встроенную форму комментария:
Я пробовал что-то подобное в запросе POST почтальона:
{
"title":"post with comments",
"body":"some text",
"comments":[
{"body":"comment1","likeCount":"5"},
{"body":"comment2","likeCount":"8"}
]
}
Но я получаю это:
{ "code": 400, "message": "Validation Failed", "errors": { "errors": [ "This form should not contain extra fields." ], "children": { "title": {}, "body": {}, "comments": {} } } }
Вопрос в том:
Как должен выглядеть запрос json?
Редактировать:
Я не думаю, что проблема в корневых ключах, потому что перед установкой отношения ManyToOne я отправлял форму следующим образом:
{
"title":"post with comments",
"body":"some text"
}
дополнительная информация:
Это код контроллера:
$form = $this->formFactory->create(PublicationType::class, new Publication());
$form->submit($request->request->all());
if (false === $form->isValid()) {
return $this->viewhandler->createResponse($this->view($form),$request,'json');
}
$this->entityManager->persist($form->getData());
$this->entityManager->flush();
return new JsonResponse(
[
'status' => 'ok',
]
);
просто повторю, если это не было очевидно: не работает не встроенная форма, а базовая форма (публикация). компонент формы жалуется на «лишние» ключи, и все упомянутые ключи взяты из базовой формы (ключи корневого уровня)
Я отредактировал вопрос. Любая другая информация?
возможно, это не имеет значения, но наличие публикации в качестве поля в форме комментария кажется странным, также, возможно, вам понадобятся allow_add и allow_delete?
Я добавил allow_add и allow_delete в метод configureOptions в PublicationType и получил ту же ошибку.
Плохо, я неправильно добавил allow_add.
значит ли это, что ваша проблема решена?
да, мне пришлось сделать немного больше, потому что у меня были проблемы с устойчивостью каскада, но теперь это решено. Как вы сказали, «Эта форма не должна содержать лишних полей». ошибка была решена добавлением "allow_add"=true в: >add('comments', CollectionType::class, array( 'entry_type' => CommentType::class, 'allow_add' => true, 'by_reference' => false, ' entry_options' => ['label' => false], ));
Приятно. рад, что мое настойчивое нытье направило вас в правильном направлении.
Спасибо, Джакуми был действительно полезен. Я думаю, вы должны опубликовать свое предложение о alow_add и allow_delete в качестве ответа для записей.

Вопреки моему первому предположению, сообщение об ошибке действительно содержит поля это было ожидаемо:
{
"code": 400,
"message": "Validation Failed",
"errors": {
"errors": [
"This form should not contain extra fields."
],
"children": {
"title": {},
"body": {},
"comments": {}
}
}
}
Однако это сообщение не особенно полезно, когда указаны именно эти поля, а проблема заключается в коллекции (подформа/подполя).
Однако CollectionType для comments не позволяет добавлять или удалять дочерние элементы (comments), если это не настроено. Добавление allow_add (и, возможно, allow_delete) устраняет проблему.
хотите показать функцию контроллера? однако мой инстинкт подсказывал бы, что у формы есть «имя», например «публикация», и, таким образом, можно ожидать, что данные будут находиться под ключом публикации или даже под публикацией_form. у вас могут быть лучшие результаты, если вместо этого вы создадите безымянную форму. посмотрите направо над раздел "заключительные мысли" на symfony.com/doc/current/forms.html#creating-form-classes