Request a linked field in symfony - php

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

Related

A circular reference has been detected when serializing the object of class "App\Entity\Catalog" (configured limit: 1)

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

Symfony 4.4 Easyadmin: set permissions to access only my owned entities

I have a colleague entity, which has a many to one relation with user entity.
I want to only have the ability to access colleagues attached to identified user.
This is for all CRUD permissions: list, edit, update, delete.
I've tried a lot of things, like DQL filter in easy_admin.yaml, but I can't manage to get authenticated user id.
I'm a Symfony junior, so I don't know how to do this and I must use Easyadmin.
So, it seems I can't use ColleagueController.php. Maybe with ColleagueRepository.php?
For the moment, everything is configured in easy_admin.yaml:
easy_admin:
design:
templates:
label_null: 'null_value.html.twig'
entities:
Colleague:
class: App\Entity\Colleague
list:
# dql_filter: "entity.user = 15"
# dql_filter: "entity.user = '%env(AUTHENTICATED_USER)%'"
# dql_filter: "entity.user = (SELECT id FROM user WHERE email = '%env(AUTHENTICATED_USER)%')"
# dql_filter: "entity.user = (SELECT id FROM App\Entity\User WHERE email = 'aaa#gmail.com')"
fields:
- user
- name
- role
- notes
- { property: 'thumbnail', type: 'image', base_path: '%uploads_path%' }
actions: ['show', 'edit', 'delete']
form:
fields:
- user
- name
- role
- notes
- { property: 'thumbnailFile', type: 'vich_image' }
show:
fields:
- user
- name
- role
- notes
- { property: 'thumbnail', type: 'image', base_path: '%uploads_path%' }
And my Entity\Colleague.php:
<?php
namespace App\Entity;
use App\Repository\ColleagueRepository;
use Doctrine\ORM\Mapping as ORM;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;
/**
* #ORM\Entity(repositoryClass=ColleagueRepository::class)
* #Vich\Uploadable
*/
class Colleague
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="colleagues")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $role;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $notes;
/**
* #ORM\Column(type="string", length=255, nullable=true, options={"default": 0})
*
* #var string
*/
private $thumbnail;
/**
* #Vich\UploadableField(mapping="colleague_thumbnails", fileNameProperty="thumbnail")
*
* #var File
*/
private $thumbnailFile;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
private $createdAt;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
private $updatedAt;
public function __construct()
{
$this->setCreatedAt(new \DateTime());
$this->setUpdatedAt(new \DateTime());
// var_dump($this->get('security.token_storage')->getToken()->getUser());
// die;
}
public function getId(): ?int
{
return $this->id;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getRole(): ?string
{
return $this->role;
}
public function setRole(?string $role): self
{
$this->role = $role;
return $this;
}
public function getNotes(): ?string
{
return $this->notes;
}
public function setNotes(?string $notes): self
{
$this->notes = $notes;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getThumbnail(): ?string
{
return $this->thumbnail;
}
public function setThumbnail(?string $thumbnail): self
{
$this->thumbnail = $thumbnail;
return $this;
}
/**
* #return File
*/
public function getThumbnailFile()
{
return $this->thumbnailFile;
}
/**
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*
* #return User
*/
public function setThumbnailFile(File $thumbnail = null)
{
$this->thumbnailFile = $thumbnail;
if ($thumbnail) {
$this->updatedAt = new \DateTime('now');
}
return $this;
}
}
Thanks in advance for your precious help.
Here is the same answer as proposed on Linkedin: easy admin advanced permissions. (french post content)
You can combine an event subscriber with a voter, simply follow this example.
Best regards.
I've managed to do Easyadmin specific filtering this way:
config/packages/easy_admin.yaml:
easy_admin:
entities:
Colleague:
class: App\Entity\Colleague
controller: App\Controller\ColleagueController
src/Controller/ColleagueController.php:
<?php
namespace App\Controller;
use Symfony\Component\Routing\Annotation\Route;
use EasyCorp\Bundle\EasyAdminBundle\Controller\EasyAdminController;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class ColleagueController extends EasyAdminController
{
protected function createListQueryBuilder($entityClass, $sortDirection, $sortField = null, $dqlFilter = null)
{
$result = parent::createListQueryBuilder($entityClass, $sortDirection, $sortField, $dqlFilter);
if (method_exists($entityClass, 'getUser')) {
$result->andWhere('entity.user = :user');
$result->setParameter('user', $this->getUser());
}
return $result;
}
protected function createSearchQueryBuilder($entityClass, $searchQuery, array $searchableFields, $sortField = null, $sortDirection = null, $dqlFilter = null)
{
$result = parent::createSearchQueryBuilder($entityClass, $searchQuery, $searchableFields, $sortField, $sortDirection, $dqlFilter);
if (method_exists($entityClass, 'getUser')) {
$result->andWhere('entity.user = :user');
$result->setParameter('user', $this->getUser());
}
return $result;
}
protected function createEditForm($entity, array $entityProperties)
{
$result = parent::createEditForm($entity, $entityProperties);
if ($entity->getUser() !== $this->getUser()) {
throw new AccessDeniedException();
}
return $result;
}
protected function showAction()
{
$easyadmin = $this->request->attributes->get('easyadmin');
$entity = $easyadmin['item'];
if ($entity->getUser() !== $this->getUser()) {
throw new AccessDeniedException();
}
$result = parent::showAction();
return $result;
}
protected function deleteAction()
{
$easyadmin = $this->request->attributes->get('easyadmin');
$entity = $easyadmin['item'];
if ($entity->getUser() !== $this->getUser()) {
throw new AccessDeniedException();
}
$result = parent::deleteAction();
return $result;
}
/**
* Create a colleague.
*/
protected function persistEntity($entity)
{
$entity->setUser($this->getUser());
$result = parent::persistEntity($entity);
return $result;
}
}

Doctrine ORM: composite and foreign keys as primary key

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

symfony CRUD Insert json, some doesn't get inserted

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.

how to create an object with a user attached in symfony

I'm trying to create a product that the current authenticated user created.
The user has a relationship with the product entity, i need to make a way for a user to create product with the user associated with it.
I'm following this tutorial, but it doesn't cover how to store a product with a current user
This is what i have so far
ProductController.php
public function create(Request $request)
{
$category = new Category();
$category->setName($request->get('category'));
$user = new User();
// how would i get the current user and set it to a product.
$entityManager = $this->getDoctrine()->getManager();
$product = new Product();
$product->setName($request->get('title'));
$product->setPrice($request->get('price'));
$product->setDescription($request->get('description'));
$product->setCategory($category);
$entityManager->persist($category);
$entityManager->persist($product);
$entityManager->flush();
return $this->redirectToRoute('products');
}
Should i use this method or try something else ?
Entity\User.php
public function addProduct(Product $product): self
{
if (!$this->products->contains($product)) {
$this->products[] = $product;
$product->setUser($this);
}
return $this;
}
Entity\Product.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\ProductRepository")
*/
class Product
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=190)
*/
private $name;
/**
* #ORM\Column(type="integer")
*/
private $price;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="products")
* #ORM\JoinColumn(nullable=false)
*/
private $category;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="products")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
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 getPrice(): ?int
{
return $this->price;
}
public function setPrice(int $price): self
{
$this->price = $price;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getCategory(): ?Category
{
return $this->category;
}
public function setCategory(?Category $category): self
{
$this->category = $category;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
As I can see, your relation works through addProduct() method, but in your controller, you don't call addProduct() anywhere.
try following...
$entityManager->persist($user) // you forgot to persist a new User
$entityManager->persist($category);
$entityManager->persist($product);
$user->addProduct(product)
$entityManager->flush(); // now try to flush...
Offtopic and a bit
constructive criticism
Just by looking at your controller I assume you playing around with Symfony and in particular with doctrine. If it is so, consider following. If not then just ignore it ;)
rename Product->user to Product->createdBy This naming convention makes it more obvious.
For that case it's better to switch from a bidirectional relation (your current state) to a unidirectional (google for it, but in short -> just throw away inversedBy and mappedBy part in you Product <-> User relation )

Categories