'Symfony6 FindBy and entity many to many
I use symfony6 for the first time with php 8.0.17. I want to make a small form that allows me to retrieve "Card" objects. My "Card" objects are bound to many to many color properties. I thought I could retrieve the cards objects via a findBy but I have the following error: Warning: Trying to access array offset on value of type null. I looked at the documentation but I don't understand where my error comes from.
Cannot use findBy with related object ? Or maybe it's not a good idea to use entity field in form ? As you see the code is simple and it's that i don't understand it's not okay
The code is here :
My Card entity :
<?php
namespace App\Entity;
use App\Repository\CardRepository;
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: CardRepository::class)]
class Card
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\ManyToMany(targetEntity: Color::class, inversedBy: 'card')]
private $color;
public function __construct()
{
$this->color = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* @return Collection<int, Color>
*/
public function getColor(): Collection
{
return $this->color;
}
public function addColor(Color $color): self
{
if (!$this->color->contains($color)) {
$this->color[] = $color;
}
return $this;
}
public function removeColor(Color $color): self
{
$this->color->removeElement($color);
return $this;
}
}
My Color entity :
<?php
namespace App\Entity;
use App\Repository\ColorRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ColorRepository::class)]
class Color
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $name;
#[ORM\ManyToMany(targetEntity: Card::class, mappedBy: 'color')]
private $card;
public function __construct()
{
$this->card = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function __toString()
{
return $this->getName();
}
/**
* @return Collection<int, Card>
*/
public function getCard(): Collection
{
return $this->card;
}
public function addCard(Cards $card): self
{
if (!$this->card->contains($card)) {
$this->card[] = $card;
$card->addColor($this);
}
return $this;
}
public function removeCard(Cards $card): self
{
if ($this->cards->removeElement($card)) {
$card->removeColor($this);
}
return $this;
}
}
My Search Form :
<?php
namespace App\Form;
use App\Entity\Color;
use App\Entity\Keyword;
use App\Entity\Type;
use App\Form\DataTransformer\ColorToStringTransformer;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
class CardSearchType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['required' => false])
->add('color', EntityType::class, [
'class' => Color::class,
// uses the Cards.name property as the visible option string
'choice_label' => 'name',
// used to render a select box, check boxes or radios
// 'multiple' => true,
// 'expanded' => true,
// To add little test for empty value
'placeholder' => 'Choose a color.',
'required' => false,
])
->add('save', SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver): void
{
}
}
And to finish the controller action :
class DeckBuilderController extends AbstractController
{
#[Route('/deck/builder', name: 'app_deck_builder')]
public function index(Request $request, CardRepository $cardRepository): Response
{
$form = $this->createForm(CardSearchType::class);
$form->handleRequest($request);
$cards = null;
if ($form->isSubmitted() && $form->isValid())
{
// $form->getData() holds the submitted values
$cardData = $form->getData();
$cards = $cardRepository->findBy($cardData); => error Warning: Trying to access array offset on value of type null
}
return $this->render('deck_builder/index.html.twig', [
'controller_name' => 'DeckBuilderController',
'form' => $form->createView(),
'cards' => $cards,
]);
}
Thank in advance for your response.
Solution 1:[1]
$form->getData()
doesn't return an array but an object with the form content.
My guess is that you want to use $form->get('color')->getData()
instead
Correcting your code, I would try something like this :
public function index(Request $request, CardRepository $cardRepository): Response {
$cards = array();
$form = $this->createForm(CardSearchType::class);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$cards = $cardRepository->findBy(array('color'=>$form->get('color')->getData()));
}
return $this->renderForm('deck_builder/index.html.twig', array(
'controller_name' => 'DeckBuilderController',
'form' => $form,
'cards' => $cards,
));
}
Solution 2:[2]
To solve my problem I finally created a query by hand and made the join in DQL.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | k.gonzalez |