Doctrine ORM: composite and foreign keys as primary key - php

I am trying to get composite and foreign keys as primary keys working in Doctrine ORM. I know what I'm trying to do is possible because it is described here: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/tutorials/composite-primary-keys.html#composite-and-foreign-keys-as-primary-key. This is exactly my use-case: I have some products, an order and an order item.
However, doctrine orm is unable to map this relation unto the database. The current problem is that only one of the annotated \Id primary keys is reflected on the mysql database. So $producto is translated unto the database correctly as producto_id and is both a primary key and a foreign key. However, the $orden property which is annotated in the same way doesn't appear whatsoever on my database.
This seems odd because when I was first testing this feature I tried only with one of the two properties and it worked fine, however, when both properties are annotated only one seems to be parsed by the metadata parser. Furthermore, I tried to revert my project to a usable state by forgetting about the foreign keys and just have a composite primary key (like I had it before), but now the parser doesn't seem to even recognize the primary key. For example, for:
class ProductoOrden
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
private $idOrden;
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
private $idProducto;
I get:
bash-3.2$ php bin/console make:migration
In MappingException.php line 52:
No identifier/primary key specified for Entity "App\Entity\ProductoOrden". Every Entity must
have an identifier/primary key.
So I'm unable to set it up properly or to revert it to the previous state (which is the strangest of all).
I'm about to restart my whole project from scratch, because I cannot make sense of how the metadata parsing works. I am worried I have screwed up the process because I have manually erased the files at 'src\Migrations' because of similar issues before and php bin/console doctrine:migrations:version --delete --all didn't seem to work or I haven't understood the proper use of it.
In conclusion: ¿Could anyone assert if what I am trying to do with ProducoOrden is possible (maybe I'm not understanding the documentation example)? Is there any way to completely wipe out previous cache about the annotations/ schema metadata?
I've looked unto the orm:schema-tool but I don't really get how to configure it properly or why I have to configure it at all of I already have the bin/console tool on my project.
I will show all three involved classes for completeness sake, but the main problem is within ProductoOrden (Order-items).
<?php
//Products
namespace App\Entity;
use App\Repository\ProductosRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass=ProductosRepository::class)
* #UniqueEntity("idProducto", message=" {producto {{ value }}}: llave primaria violada ")
*/
class Productos
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $nombreProducto;
/**
* #ORM\Column(type="string", length=255)
*/
private $descripcionProducto;
/**
* #ORM\Column(type="string", length=255)
*/
private $urlImagen;
/**
* #ORM\Column(type="integer")
*/
private $puntosProducto;
public function getIdProducto(): ?int
{
return $this->idProducto;
}
public function getCodProducto(): ?int
{
return $this->idProducto;
}
public function setIdProducto(int $codProducto): self
{
$this->idProducto = $codProducto;
return $this;
}
public function getNombreProducto(): ?string
{
return $this->nombreProducto;
}
public function setNombreProducto(string $nombreProducto): self
{
$this->nombreProducto = $nombreProducto;
return $this;
}
public function getDescripcionProducto(): ?string
{
return $this->descripcionProducto;
}
public function setDescripcionProducto(string $descripcionProducto): self
{
$this->descripcionProducto = $descripcionProducto;
return $this;
}
public function getUrlImagen(): ?string
{
return $this->urlImagen;
}
public function setUrlImagen(string $urlImagen): self
{
$this->urlImagen = $urlImagen;
return $this;
}
public function getPuntosProducto(): ?int
{
return $this->puntosProducto;
}
public function setPuntosProducto(int $puntosProducto): self
{
$this->puntosProducto = $puntosProducto;
return $this;
}
public function __toString(){
$str = '{producto:'.$this->getIdProducto().', nombre: '.$this->getNombreProducto().'}';
return $str;
}
}
<?php
\\Orders
namespace App\Entity;
use App\Repository\OrdenesRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass=OrdenesRepository::class)
* #UniqueEntity("idOrden", message="{orden {{ value }}}: llave primaria violada")
*/
class Ordenes
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="integer")
*/
private $totalOrden;
/**
* #ORM\Column(type="string", length=255)
*/
private $estado;
/**
* #ORM\OneToMany(targetEntity=ProductoOrden::class, mappedBy="orden", orphanRemoval=true)
*/
private $productosOrden;
public function __construct()
{
$this->productosOrden = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getIdOrden(): ?int
{
return $this->idOrden;
}
public function setIdOrden(int $idOrden): self
{
$this->idOrden = $idOrden;
return $this;
}
public function getTotalOrden(): ?int
{
return $this->totalOrden;
}
public function setTotalOrden(int $totalOrden): self
{
$this->totalOrden = $totalOrden;
return $this;
}
public function getEstado(): ?string
{
return $this->estado;
}
public function setEstado(string $estado): self
{
$this->estado = $estado;
return $this;
}
public function __toString(){
$str = '{orden:'.$this->getIdOrden().'}';
return $str;
}
/**
* #return Collection|ProductoOrden[]
*/
public function getProductosOrden(): Collection
{
return $this->productosOrden;
}
public function addProductosOrden(ProductoOrden $productosOrden): self
{
if (!$this->productosOrden->contains($productosOrden)) {
$this->productosOrden[] = $productosOrden;
$productosOrden->setOrden($this);
}
return $this;
}
public function removeProductosOrden(ProductoOrden $productosOrden): self
{
if ($this->productosOrden->contains($productosOrden)) {
$this->productosOrden->removeElement($productosOrden);
// set the owning side to null (unless already changed)
if ($productosOrden->getOrden() === $this) {
$productosOrden->setOrden(null);
}
}
return $this;
}
}
<?php
\\Order-items
namespace App\Entity;
use App\Repository\ProductoOrdenRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass=ProductoOrdenRepository::class)
* #UniqueEntity(fields={"idOrden","idProducto"}, message="{prod. orden {{ value }}}: llave primaria violada")
*/
class ProductoOrden
{
/*
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Ordenes::class, inversedBy="productosOrden")
* #ORM\JoinColumn(nullable=false)
*/
private $orden;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity=Productos::class)
* #ORM\JoinColumn(nullable=false)
*/
private $producto;
/**
* #ORM\Column(type="integer")
*/
private $puntos;
/**
* #ORM\Column(type="integer")
*/
private $cantidad;
public function getId(): ?int
{
return $this->idOrden;
}
public function setIdOrden(int $idOrden): self
{
$this ->idOrden = $idOrden;
return $this;
}
public function getIdProducto(): ?int
{
return $this->idProducto;
}
public function setIdProducto(int $idProducto): self
{
$this->idProducto = $idProducto;
return $this;
}
public function getPuntos(): ?int
{
return $this->puntos;
}
public function setPuntos(int $puntos): self
{
$this->puntos = $puntos;
return $this;
}
public function getCantidad(): ?int
{
return $this->cantidad;
}
public function setCantidad(int $cantidad): self
{
$this->cantidad = $cantidad;
return $this;
}
public function __toString(){
$str = '{productoOrden:'.$this->getId().', '.$this->getIdProducto().'}';
return $str;
}
public function getOrden(): ?Ordenes
{
return $this->orden;
}
public function setOrden(?Ordenes $orden): self
{
$this->orden = $orden;
return $this;
}
}
For the shown classes, the migration it generates is
final class Version20200814210929 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE producto_orden ADD puntos INT NOT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE producto_orden DROP puntos');
}
}
As you can see, small changes like changing the type of a property work; but it doesn't seem to take on the id() and the association annotations.
Many thanks

Related

Could not determine access type for property "image" in class "App\Entity\XXXX". Symfony 4 - EasyAdmin 3.2 - VichUploader

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

Request a linked field in symfony

I have a page that displays information about a movie. I recover in GET the id of the film. What I would like to do is retrieve the comments for each film (there is a filmId column in my table linked to the primary id of the film table)
/**
* #Route("/user/film/{id}", name="film")
*/
public function film(FilmRepository $repo, CommentRepository $comRepo, EntityManagerInterface $em, Request $req, $id)
{
$film = $repo->find($id);
$comments = $comRepo->findBy(array('id' => $id));
return $this->render('film/film.html.twig', [
'controller_name' => 'FilmController',
'film' => $film,
'comments' => $comments
]);
}
when I make a $comments = $comRepo->findBy(array('id' => $id)); I get some comments, but based on their id and NOT the film id (the comment with id 1 will be displayed on the film with id 1, but for example a comment with id 4 and the filmId a 1 will not appear on film 1, but on the film with id 4)
I tried to access the filmId field by simply making a $comments = $comRepo->findBy(array ('filmId' => $ id)); but i get the error :
An exception occurred while executing 'SELECT t0.id AS id_1, t0.content AS content_2, t0.created_at AS created_at_3, t0.author_id AS author_id_4 FROM comment t0 WHERE comment_film.film_id = ?' with params ["1"]:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'comment_film.film_id' in 'where clause'
I tried a personalized request with, in my Comment repository:
public function findAllWithFilmId($filmId)
{
$em = $this->getEntityManager();
$query = $em->createQuery(
'SELECT c
FROM App\Entity\Comment c
WHERE c.filmId = :filmId'
)->setParameter('filmId', $filmId);
return $query->getResult();
}
But it doesn't seem to work..
Where do I go to make a request like this ?
How to modify the request, which seems erroneous, from symfony without disorganizing everything? or is there a better method to correct the problem?
This is my Comment Entity
<?php
namespace App\Entity;
use App\Entity\Film;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\CommentRepository")
*/
class Comment
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="comments")
* #ORM\JoinColumn(nullable=false)
*/
private $author;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Film", inversedBy="comments")
* #ORM\JoinColumn(nullable=false)
*/
private $filmId;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
public function getId(): ?int
{
return $this->id;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): self
{
$this->author = $author;
return $this;
}
public function getFilmId(): ?Film
{
return $this->filmId;
}
public function setFilmId(?Film $filmId): self
{
$this->filmId = $filmId;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
}
I think it is possible that the error comes from annotations, because starting on symfony during the make: entity, I defined types relations which I corrected later in phpmyadmin, but not the code. For example we can see that filmId is in ManyToMany, but I think it should be in OneToOne (FilmId can only have one id and an id can only correspond to one filmId), but I'm afraid that if I change certain things it breaks everything.
If you have set up your ORM relations correctly, it should be as simple as:
$film = $repo->find($id);
$comments = $film->getComments();
You might be missing a mapping in Film.php.
Here's an XML example, should be easy enough to convert to annotations:
In film:
<one-to-many field="comments" target-entity="App\...\Comments" mapped-by="film"/>
In comments:
<many-to-one field="film" target-entity="App\...\Film" inversed-by="comments"/>
First of all, I advise you to read more about the relations between entities.
Because, the current annotations says that you can have a lot of comments on many films. It's not right. One comment may belong to one film. One movie can have many comments.
Also, I want to note that, as far as I know, #JoinColumn should be in a child entity, that is, where the link to FK is contained.
Therefore, your entities should look like this:
Comment:
<?php
namespace App\Entity;
use App\Entity\Film;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\CommentRepository")
*/
class Comment
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="comments")
*/
private $author;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Film", inversedBy="comments")
* Here we set property for our table and property of foreign table to map our comment to the right film
* nullable, because comment couldn't be without film
* #ORM\JoinColumn(name="film_id", referencedColumnName="id", nullable=false)
*/
private $film;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
public function getId(): ?int
{
return $this->id;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): self
{
$this->author = $author;
return $this;
}
public function getFilmId(): ?Film
{
return $this->filmId;
}
public function setFilmId(?Film $filmId): self
{
$this->filmId = $filmId;
return $this;
}
public function getContent(): ?string
{
return $this->content;
}
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
public function getCreatedAt(): ?DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
}
Film:
<?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\FilmRepository")
*/
class Film
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="film")
*/
private $comments;
public function __construct()
{
$this->comments = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
public function getComments(): Collection
{
return $this->comments;
}
public function setComments(Collection $comments): Film
{
$this->comments = $comments;
return $this;
}
}
So, now, you can retrieve your comments via:
/**
* #Route("/user/film/{id}", name="film")
*/
public function film($id)
{
/** #var null|EntityManager $entityManager */
$entityManager = $this->get('doctrine.orm.entity_manager');
if (null == ($film = $entityManager->getRepository(Film::class)->find($id))){
throw new NotFoundHttpException('Film not found');
}
$comments = $film->getComments();
return $this->render('film/film.html.twig', [
'film' => $film,
'comments' => $comments
]);
}

Error: Expected argument of type "int", "App\Entity\Provincie" given at property path "provincie_id" [duplicate]

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;
}

Symfony Expected argument of type "integer or null"

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;
}

Symfony 4 - FOSUserBundle - Render user information on custom route

I have a constructor and route in my custom ProfileController
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
/**
* #Route("/profile/bookings", name="profile_bookings")
*/
public function bookings()
{
$user = $this->getUser();
return $this->render('profile/bookings/bookings.html.twig', array('user'=>$user));
}
And in my template I reference
{{ user.first_name }}
But I get the error:
HTTP 500 Internal Server Error
Neither the property "first_name" nor one of the methods "first_name()", "getfirst_name()"/"isfirst_name()"/"hasfirst_name()" or "__call()" exist and have public access in class "App\Entity\User".
How do I get the user info from db and display in sub pages of profile?
Edit: User Entity ...
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
/**
* #ORM\Entity
* #ORM\Table(name="`user`")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\Column(type="string", length=190)
*/
private $first_name;
/**
* #ORM\Column(type="string", length=190)
*/
private $last_name;
/**
* #ORM\Column(type="string", length=190, nullable=true)
*/
private $phone_number;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $profile_height;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $profile_weight;
/**
* #ORM\Column(type="date", nullable=true)
*/
private $profile_dob;
/**
* #ORM\Column(type="string", length=190, nullable=true)
*/
private $profile_gender;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Booking", mappedBy="user")
*/
private $bookings;
public function __construct()
{
parent::__construct();
$this->bookings = new ArrayCollection();
}
/**
* Overridde setEmail method so that username is now optional
*
* #param string $email
* #return User
*/
public function setEmail($email)
{
$this->setUsername($email);
return parent::setEmail($email);
}
public function getFirstName()
{
return $this->first_name;
}
public function setFirstName($first_name)
{
$this->first_name = $first_name;
}
public function getLastName()
{
return $this->last_name;
}
public function setLastName($last_name)
{
$this->last_name = $last_name;
}
public function getPhoneNumber(): ?string
{
return $this->phone_number;
}
public function setPhoneNumber(string $phone_number): self
{
$this->phone_number = $phone_number;
return $this;
}
public function getProfileHeight(): ?int
{
return $this->profile_height;
}
public function setProfileHeight(?int $profile_height): self
{
$this->profile_height = $profile_height;
return $this;
}
public function getProfileDob(): ?\DateTimeInterface
{
return $this->profile_dob;
}
public function setProfileDob(?\DateTimeInterface $profile_dob): self
{
$this->profile_dob = $profile_dob;
return $this;
}
public function getProfileWeight(): ?int
{
return $this->profile_weight;
}
public function setProfileWeight(?int $profile_weight): self
{
$this->profile_weight = $profile_weight;
return $this;
}
public function getProfileGender(): ?string
{
return $this->profile_gender;
}
public function setProfileGender(?string $profile_gender): self
{
$this->profile_gender = $profile_gender;
return $this;
}
/**
* #return Collection|Booking[]
*/
public function getBookings(): Collection
{
return $this->bookings;
}
public function addBooking(Booking $booking): self
{
if (!$this->bookings->contains($booking)) {
$this->bookings[] = $booking;
$booking->setUser($this);
}
return $this;
}
public function removeBooking(Booking $booking): self
{
if ($this->bookings->contains($booking)) {
$this->bookings->removeElement($booking);
// set the owning side to null (unless already changed)
if ($booking->getUser() === $this) {
$booking->setUser(null);
}
}
return $this;
}
}
Thanks.
#Franck Gamess is right but you can also get rid of the get.
If you write {{ user.firstName }}, twig will associate that to your method getFirstName() automatically.
I don't know why you write your properties with snake_case but you could change it to camelCase and access your properties via their "real" name.
Just use in your twig template:
{{ user.getFirstName }}
It works fine. Normally what Twig does is quite simple on the PHP Layer:
check if user is an array and first_name a valid element;
if not, and if user is an object, check that first_name is a valid property;
if not, and if user is an object, check that first_name is a valid method (even if first_name is the constructor - use __construct() instead);
if not, and if user is an object, check that getfirst_name is a valid method;
if not, and if user is an object, check that isfirst_name is a valid method;
if not, and if user is an object, check that hasfirst_name is a valid method;
if not, return a null value.
See Twig variables.
By the way you should follow the Symfony Coding Standard for your variable, because it can be difficult for twig to find value of properties written in snake_case.
I don't think you should construct the UserManagerInterface in your controller. Also, like Franck says, use the coding standard if you can, it will save a lot of time and frustration in the future!
Here is the controller I use in a Symfony 4 project:
namespace App\Controller;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
/**
* #Route("/profile/bookings", name="profile_bookings")
*/
public function bookings()
{
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
return $this->render('profile/bookings/bookings.html.twig', array(
'user' => $user,
));
}
}

Categories