So I'm trying to create a method that inserts Recipies to MySQL database.
The schema of my project that a recipe (Recette) has many Ingredients.
The Recette has a Title(titre), Subtitle(Soustitre) and Ingredients that must be inserted in "POST" Request I'll show you my code, my "POST" request in Postman and the Result I get.
Also, My same code has an update function that doesn't work as well and it's always the problem of the ingredients.
This is my Controller:
namespace App\Controller;
use App\Entity\Ingredients;
use App\Entity\Recettes;
use Doctrine\ORM\EntityManagerInterface;
use http\Env\Response;
use Psr\Container\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\RecettesRepository;
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ApiRecetteController extends AbstractController
{
/**
* #Route("/api/recette", name="api_recette_index", methods={"GET"})
*/
public function index(RecettesRepository $recettesRepository)
{
return $this->json($recettesRepository->findAll(), 200, [], ['groups' => 'recette:read']);
}
/**
* #Route("/api/recette", name="api_recette_addRecettes", methods={"POST"})
*/
public function addRecettes(Request $request, SerializerInterface $serializer, EntityManagerInterface
$entityManager)
{
$jsonRecu = $request->getContent();
try {
$recette = $serializer->deserialize($jsonRecu, Recettes::class,"json");
$entityManager->persist($recette);
$entityManager->flush();
return $this->json($recette,201);
}
catch (NotEncodableValueException $e){
return $this->json([
'status'=>400,
'message' =>$e->getMessage()
],400);
}
}
/**
* #Route("/api/recette/Update/{id}", name="api_recette_updateRecettes", methods={"PUT"})
*/
public function UpdateRecettes($id, RecettesRepository $recettesRepository, Request $request,
EntityManagerInterface $entityManager)
{
$entityManger = $this->getDoctrine()->getManager();
$recettes = $entityManger->getRepository(Recettes::class)->find($id);
$data = json_decode($request->getContent(), true);
if (!$recettes) {
throw $this->createNotFoundException(
'Aucune recette trouvé pour id' . $id
);
}
empty($data['Titre']) ? true : $recettes->setTitre($data['Titre']);
empty($data['Soustitre']) ? true : $recettes->setSoustitre($data['Soustitre']);
$entityManager->persist($recettes);
$entityManger->flush();
return $this->json($recettes,204, [], ['groups' => 'recette:read']);
}
/**
* #Route("/api/recette/{id}", name="api_recette_DeleteRecettes", methods={"DELETE"})
*/
public function DeleteRecettes($id, EntityManagerInterface $entityManager, Request $request)
{
$recettes = $entityManager->getRepository(Recettes::class)->find($id);
$entityManager->remove($recettes);
$entityManager->flush();
return $this->json($recettes,202, [], ['groups' => 'recette:read']);
}
}
and My Two Entities
<?PHP
namespace App\Entity;
use App\Repository\IngredientsRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ORM\Entity(repositoryClass="App\Repository\IngredientsRepository",
repositoryClass=IngredientsRepository::class)
*/
class Ingredients
{
/**
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
* #Groups("recette:read")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Groups("recette:read")
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity=Recettes::class, inversedBy="ingredients")
* #ORM\JoinColumn(nullable=false)
*/
private $recettes;
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 getRecettes(): ?Recettes
{
return $this->recettes;
}
public function setRecettes(?Recettes $recettes): self
{
$this->recettes = $recettes;
return $this;
}
public function __toString(): ?string
{
return $this->getName();
}
}
And Recettes
<?PHP
namespace App\Entity;
use App\Repository\RecettesRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ORM\Entity(repositoryClass=RecettesRepository::class)
*/
class Recettes
{
/**
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
* #Groups("recette:read")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Groups("recette:read")
*/
private $Titre;
/**
* #ORM\Column(type="string", length=255, nullable=true)
* #Groups("recette:read")
*/
private $Soustitre;
/**
* #ORM\OneToMany(targetEntity=Ingredients::class, mappedBy="recettes")
*/
private $ingredients;
public function __construct()
{
$this->ingredients = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitre(): ?string
{
return $this->Titre;
}
public function setTitre(string $Titre): self
{
$this->Titre = $Titre;
return $this;
}
public function getSoustitre(): ?string
{
return $this->Soustitre;
}
public function setSoustitre(?string $Soustitre): self
{
$this->Soustitre = $Soustitre;
return $this;
}
/**
* #return Collection|Ingredients[]
*/
public function getingredients(): Collection
{
return $this->ingredients;
}
public function addingredients(Ingredients $ingredients): self
{
if (!$this->ingredients->contains($ingredients)) {
$this->ingredients[] = $ingredients;
$ingredients->setRecettes($this);
}
return $this;
}
public function removeingredients(Ingredients $ingredients): self
{
if ($this->ingredients->contains($ingredients)) {
$this->ingredients->removeElement($ingredients);
// set the owning side to null (unless already changed)
if ($ingredients->getRecettes() === $this) {
$ingredients->setRecettes(null);
}
}
return $this;
}
}
My Request json
JSON Row Request
My Response with 201 ok status but empty Ingredients inserted
Response From Server
To insert related entities you need to set cascade={"persist"} to your relation, ie:
/**
* #ORM\OneToMany(targetEntity=Ingredients::class, mappedBy="recettes", cascade={"persist"})
*/
private $ingredients;
Else you will need to persist the Recettes first, get the id and set it on your Ingredientss before persisting them.
Related
This aremy 2 entitites
<?php
namespace App\Entity;
use App\Repository\CatalogRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=CatalogRepository::class)
*/
class Catalog
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity=Title::class, mappedBy="Catalog", orphanRemoval=true)
*/
private $titles;
public function __construct()
{
$this->titles = 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;
}
/**
* #return Collection<int, Title>
*/
public function getTitles(): Collection
{
return $this->titles;
}
public function addTitle(Title $title): self
{
if (!$this->titles->contains($title)) {
$this->titles[] = $title;
$title->setCatalog($this);
}
return $this;
}
public function removeTitle(Title $title): self
{
if ($this->titles->removeElement($title)) {
// set the owning side to null (unless already changed)
if ($title->getCatalog() === $this) {
$title->setCatalog(null);
}
}
return $this;
}
}
and
<?php
namespace App\Entity;
use App\Repository\TitleRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=TitleRepository::class)
*/
class Title
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $year;
/**
* #ORM\Column(type="float", nullable=true)
*/
private $rating;
/**
* #ORM\ManyToOne(targetEntity=Catalog::class, inversedBy="titles")
* #ORM\JoinColumn(nullable=false)
*/
private $Catalog;
public function __construct()
{
$this->Catalog = 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 getYear(): ?int
{
return $this->year;
}
public function setYear(?int $year): self
{
$this->year = $year;
return $this;
}
public function getRating(): ?float
{
return $this->rating;
}
public function setRating(?float $rating): self
{
$this->rating = $rating;
return $this;
}
public function getCatalog(): ?Catalog
{
return $this->Catalog;
}
public function setCatalog(?Catalog $Catalog): self
{
$this->Catalog = $Catalog;
return $this;
}
}
I when I try to seralize it
$em = $doctrine->getManager();
$catalogs = $em->getRepository(Catalog::class)->findAll();
$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$json = $serializer->serialize($catalogs, 'json', ['groups' => ['title','catalog']]);
I get this error
A circular reference has been detected when serializing the object of class "App\Entity\Catalog" (configured limit: 1).
Any way to avoid this problem??I know that catalor references title and totle recerences catalog but I think its the correct way to build the relation but it doesn't work for serialization, shoud I change shomething in the relation or I can serialize it in another way
UPDATE:
I tried with ignore and groups but I get the same error
at catalog
/**
* #ORM\Column(type="float")
* #Groups({"group1"})
*/
private $rating;
/**
* #ORM\ManyToOne(targetEntity=Catalog::class, inversedBy="titles")
* #ORM\JoinColumn(nullable=false)
* #Ignore()
*/
private $Catalog;
and
$json = $serializer->serialize($catalogs, 'json', ['groups' => 'group1']);
Sibling question here
A circular reference has been detected when serializing the object of class "App\Entity\User" (configured limit: 1)
You can use the group concept as described in the official documentation
You can also create a circular reference handler to handle it. For exemple in your controller you can serialize the Catalog entity like :
<?php
namespace App\Controller;
use App\Repository\CatalogRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
class MainController extends AbstractController
{
#[Route('/', name: 'app_main')]
public function index(CatalogRepository $catalogRepository, SerializerInterface $serializer): JsonResponse
{
$catalogs = $catalogRepository->findAll();
$circularRefHandler = fn($catalog, $format, $context)=> $catalog->getName();
$context = [
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => $circularRefHandler
];
$catalogsJson = $serializer->serialize($catalogs, 'json', $context);
return $this->json([
'catalogs' => $catalogsJson
]);
}
}
If you want tou use the GetSetMethodNormalizer create a context with GetSetMethodNormalizerContextBuilder
$circularRefHandler = fn($catalog, $format, $context)=> $catalog->getName();
$context = [
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => $circularRefHandler
];
$contextBuilder = (new GetSetMethodNormalizerContextBuilder())
->withContext($context);
$catalogsJson = $serializer->serialize($catalogs, 'json',$contextBuilder->toArray());
The full code of this example is here
Strugling here trygin to integrate VichImageUploader into my EasyAdmin 3.2.
This version of EasyAdmin is letting us create custom Fields which works just fine.
In my case I am only trying to upload 1 image and push it into my DB. I set up my Easy Admin dashboard and just followed:
https://symfony.com/doc/2.x/bundles/EasyAdminBundle/integration/vichuploaderbundle.html
to hydrate my configureFields function inside my CrudController.
As in the docs, I made a imageFile field joint to a image field althogeter with seters and geters.
Inside my CrudController I use my custom field because it seems its the only way to do image uploads in this version of easyadmin.
My CrudController
namespace App\Controller\Admin;
use App\Entity\ButtonPlant;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
use EasyCorp\Bundle\EasyAdminBundle\Field\UrlField;
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
use Vich\UploaderBundle\Form\Type\VichImageType;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Field\VichImageField;
class ButtonPlantCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return ButtonPlant::class;
}
public function configureFields(string $pageName): iterable
{
$imageFile = VichImageField::new('imageFile')->setFormType(VichImageType::class);
$image = ImageField::new('image')->setBasePath('/uploads/images');
$fields = [
TextField::new('content', 'Contenu'),
/* CollectionField::new('image')
->setEntryType(ImageType::class)
->setUploadDir('public\uploads\images\buttonplants'),
ImageField::new('imageFile')->setFormType(VichImageType::class), */
AssociationField::new('stepId', 'Etape'),
AssociationField::new('nextStepId', 'Prochaine Etape' ),
AssociationField::new('finalSheetId', 'Fiche Final'),
];
if ($pageName == Crud::PAGE_INDEX || $pageName == Crud::PAGE_DETAIL) {
$fields[] = $image;
} else {
$fields[] = $imageFile;
}
return $fields;
}
My Entity Controller
namespace App\Entity;
use App\Repository\ButtonPlantRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use DateTime;
/**
* #ORM\Entity(repositoryClass=ButtonPlantRepository::class)
* #Vich\Uploadable
*/
class ButtonPlant
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $content;
/**
* #ORM\Column(type="string", length=255)
* #var string
*/
private $image;
/**
* #Vich\UploadableField(mapping="buttonplant_images", fileNameProperty="image")
* #var File
*/
private $imageFile;
/**
* #ORM\OneToOne(targetEntity=FinalSheet::class, cascade={"persist", "remove"})
*/
private $finalSheetId;
/**
* #ORM\ManyToOne(targetEntity=CoursePlant::class, inversedBy="buttonPlants")
* #ORM\JoinColumn(nullable=false)
*/
private $stepId;
/**
* #ORM\OneToOne(targetEntity=CoursePlant::class, cascade={"persist", "remove"})
*/
private $nextStepId;
/**
* #ORM\Column(type="datetime", nullable=true)
* #var \DateTime
*/
private $updatedAt;
public function getId(): ?int
{
return $this->id;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
public function getImage(): ?string
{
return $this->image;
}
public function setIamge(string $image): self
{
$this->image = $image;
return $this;
}
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
// VERY IMPORTANT:
// 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
if ($image) {
// if 'updatedAt' is not defined in your entity, use another property
$this->updatedAt = new \DateTime('now');
}
}
public function getImageFile()
{
return $this->imageFile;
}
public function getFinalSheetId(): ?FinalSheet
{
return $this->finalSheetId;
}
public function setFinalSheetId(?FinalSheet $finalSheetId): self
{
$this->finalSheetId = $finalSheetId;
return $this;
}
public function getStepId(): ?CoursePlant
{
return $this->stepId;
}
public function setStepId(?CoursePlant $stepId): self
{
$this->stepId = $stepId;
return $this;
}
public function getNextStepId(): ?CoursePlant
{
return $this->nextStepId;
}
public function setNextStepId(?CoursePlant $nextStepId): self
{
$this->nextStepId = $nextStepId;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(?\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
}
My custom Field
namespace EasyCorp\Bundle\EasyAdminBundle\Field;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait;
use Vich\UploaderBundle\Form\Type\VichImageType;
class VichImageField implements FieldInterface
{
use FieldTrait;
public static function new(string $propertyName, ?string $label = null)
{
return (new self())
->setProperty($propertyName)
->setTemplatePath('')
->setLabel($label)
->setFormType(VichImageType::class);
}
}
And my error is
Could not determine access type for property "image" in class "App\Entity\ButtonPlant".
Thanks in advance for any help
I solved my problem deleting the field "image" and creating it back but this time is allowed to be null.
Hopefully it can be useful for anyone
Following this doc https://symfony.com/doc/current/form/data_transformers.html#harder-example-transforming-an-issue-number-into-an-issue-entity, we learn how to fill a field (which is an Entity) without using a choice field type or similar.
It seems to work, using the primary key of the Entity concerned (el famoso "id").
However, i want to use another field and store it in a table, but i have an error (see screeshot at bottom)
Entities :
<?php
namespace App\Entity;
use App\Repository\LandRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=LandRepository::class)
* #ORM\Table(name="land",indexes={#ORM\Index(columns={"uid"})})
* #ORM\HasLifecycleCallbacks()
*/
class Land
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $libelle;
/**
* #ORM\Column(type="integer")
*/
private $uid;
public function getId(): ?int
{
return $this->id;
}
public function getLibelle(): ?string
{
return $this->libelle;
}
public function setLibelle(string $libelle): self
{
$this->libelle = $libelle;
return $this;
}
public function getUid(): ?int
{
return $this->uid;
}
public function setUid(int $uid): self
{
$this->uid = $uid;
return $this;
}
}
namespace App\Entity;
use App\Repository\RideRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=RideRepository::class)
* #ORM\HasLifecycleCallbacks()
*/
class Ride
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $libelle;
/**
* #ORM\ManyToOne(targetEntity=Land::class)
* #ORM\JoinColumn(name="areaparent", referencedColumnName="uid")
*/
private $areaparent;
public function getId(): ?int
{
return $this->id;
}
public function getLibelle(): ?string
{
return $this->libelle;
}
public function setLibelle(string $libelle): self
{
$this->libelle = $libelle;
return $this;
}
public function getAreaparent(): ?Land
{
return $this->areaparent;
}
public function setAreaparent(?Land $areaparent): self
{
$this->areaparent = $areaparent;
return $this;
}
}
Expected final result :
"Land" table
"Ride" table
So you can see that in the "ride" table that the "areaparent" column is relative to the "land.uid" column.
"Land" form is classic
"Ride" form have a minor change : The "areaparent" is not a choice field, it's a text type which will call my DataTransformer
Extract of my form "RideType"
<?php
namespace App\Form;
use App\Entity\Ride;
use App\Entity\Land;
use Symfony\Component\Form\AbstractType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Repository\LandRepository;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use App\Form\DataTransformer\UIDTransformer;
class RideType extends AbstractType
{
private $transformer;
public function __construct(UIDTransformer $transformer)
{
$this->transformer = $transformer;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('libelle')
->add('areaparent', TextType::class, [
'invalid_message' => 'That is not a valid areaparent number',
])
;
$builder->get('areaparent')->addModelTransformer($this->transformer);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Ride::class,
]);
}
}
Extract of my DataTransformer :
<?php
namespace App\Form\DataTransformer;
use App\Entity\Land;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
class UIDTransformer implements DataTransformerInterface
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* Transforms an object (Land) to a string (areaparent).
*
* #param Land|null $Land
* #return string
*/
public function transform($land)
{
if (null === $land) {
return '';
}
return $land->getUid();
}
/**
* Transforms a string (police) to an object (Land).
*
* #param string $areaparent
* #return Land|null
* #throws TransformationFailedException if object (Land) is not found.
*/
public function reverseTransform($areaparent)
{
// no areaparent? It's optional, so that's ok
if (!$areaparent) {
return;
}
$land = $this->entityManager->getRepository(Land::class)->findByUid($areaparent);
if (count($land) == 0) {
// causes a validation error
// this message is not shown to the user
// see the invalid_message option
throw new TransformationFailedException(sprintf('Land with areaparent "%s" does not exist!',$areaparent));
}
return $land[0];
}
}
Error
I try to debug the symfony/doctrine code, and if i correctly understand, the error appears because the field "uid" is not considered as an "identifier".
See some dump.
Have you got some ideas ?
Thanks !
TL;TR:How to make form fields from formbuilder with relation column
Here is my Level Entity
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\LevelRepository")
*/
class Level
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=10)
*/
private $name;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $description;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Area", mappedBy="levelid", orphanRemoval=true)
*/
private $areas;
public function __construct()
{
$this->areas = 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 getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
/**
* #return Collection|Area[]
*/
public function getAreas(): Collection
{
return $this->areas;
}
public function addArea(Area $area): self
{
if (!$this->areas->contains($area)) {
$this->areas[] = $area;
$area->setLevelid($this);
}
return $this;
}
public function removeArea(Area $area): self
{
if ($this->areas->contains($area)) {
$this->areas->removeElement($area);
// set the owning side to null (unless already changed)
if ($area->getLevelid() === $this) {
$area->setLevelid(null);
}
}
return $this;
}
}
and this is my Area Entity
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\AreaRepository")
*/
class Area
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Property", mappedBy="area")
*/
private $property;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Level", inversedBy="areas")
* #ORM\JoinColumn(nullable=false)
*/
private $levelid;
public function __construct()
{
$this->property = new ArrayCollection();
$this->projects = 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;
}
/**
* #return Collection|property[]
*/
public function getProperty(): Collection
{
return $this->property;
}
public function addProperty(property $property): self
{
if (!$this->property->contains($property)) {
$this->property[] = $property;
$property->setArea($this);
}
return $this;
}
public function removeProperty(property $property): self
{
if ($this->property->contains($property)) {
$this->property->removeElement($property);
// set the owning side to null (unless already changed)
if ($property->getArea() === $this) {
$property->setArea(null);
}
}
return $this;
}
public function getLevelid(): ?Level
{
return $this->levelid;
}
public function setLevelid(?Level $levelid): self
{
$this->levelid = $levelid;
return $this;
}
}
Area connected to Level
And this is Area Form builder
<?php
namespace App\Form;
use App\Entity\Area;
// use App\Entity\District;
use App\Entity\Level;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class AreaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('Level', EntityType::class, array(
// looks for choices from this entity
'class' => Level::class,
'label' => 'Level',
// uses the User.username property as the visible option string
'choice_label' => 'name',
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Area::class,
]);
}
}
And the error is
Neither the property "Level" nor one of the methods "getLevel()", "level()", "isLevel()", "hasLevel()", "__get()" exist and have public access in class "App\Entity\Area".
Your problem is caused by form field and Entity field name mismatch. AreaType tries to refer to level property using Symfony's PropertyAccessor on Area while it does not have such field.
You should either rename your entity field from $levelid to $level (because it actually holds an Entity, not ID), changing setter and getter accordingly (recommended solution) or change form field name from Level to levelid.
You can find more information about Symfony Forms in official docummentation.
I'm having an issue, and I don't know how to fix it. I'm doing a CRUD for categories on a webiste.
We can Have 2 types of Categories, categorieParent and each Categoriehaving one categorieParent.
I've mae the CRUD with the make:form But when I submit the form the following error appear :
Expected argument of type "integer or null",
"App\Entity\CategorieParent" given at property path
"categorie_parent_id".
Here are my ENTITY :
Categorie
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\CategorieRepository")
*/
class Categorie
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $categorie_intitule;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\CategorieParent", inversedBy="categorie_id")
*/
private $categorie_parent_id;
public function __construct()
{
$this->categorie_id = new ArrayCollection();
$this->categorie_id_1 = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getCategorieIntitule(): ?string
{
return $this->categorie_intitule;
}
public function setCategorieIntitule(string $categorie_intitule): self
{
$this->categorie_intitule = $categorie_intitule;
return $this;
}
/**
* #return Collection|Produit[]
*/
public function getCategorieId1(): Collection
{
return $this->categorie_id_1;
}
public function addCategorieId1(Produit $categorieId1): self
{
if (!$this->categorie_id_1->contains($categorieId1)) {
$this->categorie_id_1[] = $categorieId1;
$categorieId1->setCategorieId1($this);
}
return $this;
}
public function removeCategorieId1(Produit $categorieId1): self
{
if ($this->categorie_id_1->contains($categorieId1)) {
$this->categorie_id_1->removeElement($categorieId1);
// set the owning side to null (unless already changed)
if ($categorieId1->getCategorieId1() === $this) {
$categorieId1->setCategorieId1(null);
}
}
return $this;
}
public function getCategorieParentId(): ?int
{
return $this->categorie_parent_id;
}
public function setCategorieParentId(?int $categorie_parent_id): self
{
$this->categorie_parent_id = $categorie_parent_id;
return $this;
}
public function addCategorieParentId(self $categorieParentId): self
{
if (!$this->categorie_parent_id->contains($categorieParentId)) {
$this->categorie_parent_id[] = $categorieParentId;
}
return $this;
}
public function removeCategorieParentId(self $categorieParentId): self
{
if ($this->categorie_parent_id->contains($categorieParentId)) {
$this->categorie_parent_id->removeElement($categorieParentId);
}
return $this;
}
}
**categorieParent **
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\CategorieParentRepository")
*/
class CategorieParent
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $categorie_intitule;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Categorie", mappedBy="categorie_parent_id")
*/
private $categorie_id;
public function __construct()
{
$this->categorie_id = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getCategorieIntitule(): ?string
{
return $this->categorie_intitule;
}
public function setCategorieIntitule(string $categorie_intitule): self
{
$this->categorie_intitule = $categorie_intitule;
return $this;
}
/**
* #return Collection|Categorie[]
*/
public function getCategorieId(): Collection
{
return $this->categorie_id;
}
public function addCategorieId(Categorie $categorieId): self
{
if (!$this->categorie_id->contains($categorieId)) {
$this->categorie_id[] = $categorieId;
$categorieId->setCategorieParentId($this);
}
return $this;
}
public function removeCategorieId(Categorie $categorieId): self
{
if ($this->categorie_id->contains($categorieId)) {
$this->categorie_id->removeElement($categorieId);
// set the owning side to null (unless already changed)
if ($categorieId->getCategorieParentId() === $this) {
$categorieId->setCategorieParentId(null);
}
}
return $this;
}
public function __toString()
{
return $this->categorie_intitule;
}
}
Can you explain me what i get wrong ? Thanks a lot.
Look at this part:
/**
* #ORM\ManyToOne(targetEntity="App\Entity\CategorieParent", inversedBy="categorie_id")
*/
private $categorie_parent_id;
While your attribute name is categorie_parent_id, it won't return an ID. Doctrine hydrates this field into an object. It will return a CategorieParent object (or null) instead. Consider removing the _id part of this attribute name, because it doesn't hold an integer but an object.
Update your methods:
public function getCategorieParentId(): ?CategorieParent
{
return $this->categorie_parent_id;
}
public function setCategorieParentId(?CategorieParent $categorie_parent_id): self
{
$this->categorie_parent_id = $categorie_parent_id;
return $this;
}