how to create an object with a user attached in symfony - php

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 )

Related

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

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

How can I connect my entity field to the same entity?

I added to my entity "Category" a field "parentcategory" to be able to connect a category to another category:
class Category
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumn(name="parentcategory", referencedColumnName="id")
*
*/
private $parentcategory;
public function getId(): ?int
{
return $this->id;
}
public function getParentcategory(): ?Parentcategory {
return $this->parentcategory;
}
public function setParentcategory(?Parentcategory $parentcategory): self {
$this->parentcategory = $parentcategory;
return $this;
}
I get the error message:
The return type of method "getParentcategory" in class
"App\Entity\Category" is invalid.
Change
public function getParentcategory(): ?Parentcategory {
return $this->parentcategory;
}
public function setParentcategory(?Parentcategory $parentcategory): self {
$this->parentcategory = $parentcategory;
return $this;
}
to
public function getParentcategory(): ?Category {
return $this->parentcategory;
}
public function setParentcategory(?Category $parentcategory): self {
$this->parentcategory = $parentcategory;
return $this;
}
Because in your case return type is invalid class
It's actually your setter that is causing issueyou need to set the exact class
public function setParentcategory(?Category $category): self {
$this->parentcategory = $category;
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 multiple entities in single form

Been trying for hours and hours to get my multi entity form to work, but it really breaks my head and none of the examples I've found work.
I checked the Collection form type documentation and form collections, as well as the Entity form type.
I have a User entity, UserRole entity and a Role entity.
UserRole contains a userID and a roleID. Just a linking table.
The form shows fields to create a User and I want to be able to as well select a new Role for the new user. So I've tried to use the EntityType, a select dropdown shows with all the roles nicely (only if i add the option mapped => false), but doesn't process after form submit.
It's data is not in the $form->getData(), the user gets created, the user_role entry never created.
If I try it without the mapped => false it throws me:
Could not determine access type for property "user_roles" in class "App\Entity\User": The property "user_roles" in class "App\Entity\User" can be defined with the methods "addUserRole()", "removeUserRole()" but the new value must be an array or an instance of \Traversable, "App\Entity\Role" given..
Code:
$form = $this->createFormBuilder(new User)
... //other add entries
->add('user_roles', EntityType::class, array(
'label' => 'Group (role)',
'class' => Role::class,
'choice_label' => 'name',
// 'mapped' => false, // Form works when false, but doesn't save/create UserRole entry
))
->getForm();
$form->handleRequest($request);
Using the CollectionType it's not showing a select dropdown at all.
Code:
$form = $this->createFormBuilder($user)
.... //other add entries
->add('user_roles', CollectionType::class, array(
'entry_type' => ChoiceType::class,
'entry_options' => array(
'choices' => $roleChoices,
),
))
->getForm();
$form->handleRequest($request);
Am I missing something in my Controller's code or do I misunderstand the use of the Form types? I really have no clue what I'm doing wrong.
User Entity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use JMS\Serializer\Annotation\Exclude;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\HasLifecycleCallbacks()
*/
class User implements UserInterface
{
/**
* #ORM\Column(type="string", length=255, nullable=true)
* #Exclude
*/
private $apiToken;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $email;
/**
* #ORM\Column(type="json_array")
*/
private $roles = [];
/**
* #ORM\Column(type="string", length=255)
*/
private $first_name;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $middle_name;
/**
* #ORM\Column(type="string", length=255)
*/
private $last_name;
/**
* #ORM\Column(type="boolean")
*/
private $enabled;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $blocked_at;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Project", mappedBy="created_by")
*/
private $projects;
/**
* #ORM\OneToMany(targetEntity="App\Entity\UserRole", mappedBy="user", fetch="EAGER")
*/
private $user_roles;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Category", mappedBy="created_by")
*/
private $categories;
/**
* #ORM\OneToMany(targetEntity="App\Entity\ProjectFileIos", mappedBy="created_by")
*/
private $projectFileIos;
/**
* #ORM\OneToMany(targetEntity="App\Entity\ProjectFileAndroid", mappedBy="created_by")
*/
private $projectFileAndroid;
/**
* Generate full name
*/
private $full_name;
/**
* #var string The hashed password
* #ORM\Column(type="string")
* #Exclude
*/
private $password;
/**
* #ORM\OneToMany(targetEntity="App\Entity\ProjectUser", mappedBy="user", fetch="EAGER")
*/
private $projectUsers;
/**
* #ORM\Column(type="datetime")
*/
private $created_at;
/**
* #ORM\Column(type="datetime")
*/
private $updated_at;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="project")
*/
private $created_by;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="project")
* #ORM\JoinColumn(nullable=true)
*/
private $last_updated_by;
public function __construct()
{
$this->user_roles = new ArrayCollection();
$this->user_role = new ArrayCollection();
$this->categories = new ArrayCollection();
$this->projectFileIos = new ArrayCollection();
$this->projectFileAndroid = new ArrayCollection();
$this->projectUsers = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getApiToken(): ?string
{
return $this->apiToken;
}
public function setApiToken(string $apiToken): self
{
$this->apiToken = $apiToken;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getFirstName(): ?string
{
return $this->first_name;
}
public function setFirstName(string $first_name): self
{
$this->first_name = $first_name;
return $this;
}
public function getMiddleName(): ?string
{
return $this->middle_name;
}
public function setMiddleName(string $middle_name): self
{
$this->middle_name = $middle_name;
return $this;
}
public function getLastName(): ?string
{
return $this->last_name;
}
public function setLastName(string $last_name): self
{
$this->last_name = $last_name;
return $this;
}
public function getEnabled(): ?bool
{
return $this->enabled;
}
public function setEnabled(bool $enabled): self
{
$this->enabled = $enabled;
return $this;
}
public function getBlockedAt(): ?\DateTimeInterface
{
return $this->blocked_at;
}
public function setBlockedAt(?\DateTimeInterface $blocked_at): self
{
$this->blocked_at = $blocked_at;
return $this;
}
/**
* #return Collection|UserRole[]
*/
public function getUserRoles(): ?Collection
{
return $this->user_roles;
}
public function getUserRole(): ?Collection
{
return $this->user_role;
}
public function addUserRole(UserRole $userRole): self
{
if (!$this->user_role->contains($userRole)) {
$this->user_role[] = $userRole;
$user_role->setUserId($this);
}
return $this;
}
public function removeUserRole(UserRole $userRole): self
{
if ($this->user_role->contains($userRole)) {
$this->user_role->removeElement($userRole);
// set the owning side to null (unless already changed)
if ($user_role->getUserId() === $this) {
$user_role->setUserId(null);
}
}
return $this;
}
/**
* #return Collection|Project[]
*/
public function getProjects(): Collection
{
return $this->projects;
}
public function addProject(Project $project): self
{
if (!$this->project->contains($project)) {
$this->project[] = $project;
$project->setUserId($this);
}
return $this;
}
public function removeProject(Project $project): self
{
if ($this->project->contains($project)) {
$this->project->removeElement($project);
// set the owning side to null (unless already changed)
if ($project->getUserId() === $this) {
$project->setUserId(null);
}
}
return $this;
}
/**
* #return Collection|Category[]
*/
public function getCategories(): Collection
{
return $this->categories;
}
public function addCategory(Category $category): self
{
if (!$this->categories->contains($category)) {
$this->categories[] = $category;
$category->setCreatedBy($this);
}
return $this;
}
public function removeCategory(Category $category): self
{
if ($this->categories->contains($category)) {
$this->categories->removeElement($category);
// set the owning side to null (unless already changed)
if ($category->getCreatedBy() === $this) {
$category->setCreatedBy(null);
}
}
return $this;
}
/**
* #return Collection|ProjectFileIos[]
*/
public function getProjectFileIos(): Collection
{
return $this->projectFileIos;
}
public function addProjectFileIo(ProjectFileIos $projectFileIo): self
{
if (!$this->projectFileIos->contains($projectFileIo)) {
$this->projectFileIos[] = $projectFileIo;
$projectFileIo->setCreatedBy($this);
}
return $this;
}
public function removeProjectFileIo(ProjectFileIos $projectFileIo): self
{
if ($this->projectFileIos->contains($projectFileIo)) {
$this->projectFileIos->removeElement($projectFileIo);
// set the owning side to null (unless already changed)
if ($projectFileIo->getCreatedBy() === $this) {
$projectFileIo->setCreatedBy(null);
}
}
return $this;
}
/**
* #return Collection|ProjectFileAndroid[]
*/
public function getProjectFileAndroid(): Collection
{
return $this->projectFileAndroid;
}
public function addProjectFileAndroid(ProjectFileAndroid $projectFileAndroid): self
{
if (!$this->projectFileAndroid->contains($projectFileAndroid)) {
$this->projectFileAndroid[] = $projectFileAndroid;
$projectFileAndroid->setCreatedBy($this);
}
return $this;
}
public function removeProjectFileAndroid(ProjectFileAndroid $projectFileAndroid): self
{
if ($this->projectFileAndroid->contains($projectFileAndroid)) {
$this->projectFileAndroid->removeElement($projectFileAndroid);
// set the owning side to null (unless already changed)
if ($projectFileAndroid->getCreatedBy() === $this) {
$projectFileAndroid->setCreatedBy(null);
}
}
return $this;
}
public function getFullName()
{
$lastName = $this->middle_name ? $this->middle_name . ' ' : '';
$lastName .= $this->last_name;
return $this->first_name . ' ' . $lastName;
}
/**
* Triggered after entity has been loaded into the current EntityManager from de database
* or after refresh operation applied to it
* #ORM\PostLoad
*/
public function postLoad()
{
$this->full_name = $this->getFullName();
}
/**
* #return Collection|ProjectUser[]
*/
public function getProjectUsers(): Collection
{
return $this->projectUsers;
}
public function addProjectUser(ProjectUser $projectUser): self
{
if (!$this->projectUsers->contains($projectUser)) {
$this->projectUsers[] = $projectUser;
$projectUser->setUser($this);
}
return $this;
}
public function removeProjectUser(ProjectUser $projectUser): self
{
if ($this->projectUsers->contains($projectUser)) {
$this->projectUsers->removeElement($projectUser);
// set the owning side to null (unless already changed)
if ($projectUser->getUser() === $this) {
$projectUser->setUser(null);
}
}
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->created_at;
}
public function setCreatedAt(\DateTimeInterface $created_at): self
{
$this->created_at = $created_at;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updated_at;
}
public function setUpdatedAt(\DateTimeInterface $updated_at): self
{
$this->updated_at = $updated_at;
return $this;
}
public function getCreatedBy(): ?User
{
return $this->created_by;
}
public function setCreatedBy(?User $created_by): self
{
$this->created_by = $created_by;
return $this;
}
public function getLastUpdatedBy(): ?User
{
return $this->last_updated_by;
}
public function setLastUpdatedBy(?User $last_updated_by): self
{
$this->last_updated_by = $last_updated_by;
return $this;
}
/**
* Triggered on insert
* #ORM\PrePersist
*/
public function onPrePersist()
{
$this->enabled = true;
$this->created_at = new \DateTime("now");
$this->updated_at = new \DateTime();
$this->roles = 'a:1:{i:0;s:9:"ROLE_USER";}';
}
/**
* Triggered on update
* #ORM\PreUpdate
*/
public function onPreUpdate()
{
$this->updated_at = new \DateTime("now");
}
}
In Symfony, to get non-mapped form data, try doing like this.
$data = $form->getData();
$roles = $form->get("user_roles")->getData();
Also, noted one thing. Shouldn't the class be UserRole::class instead of Role::class, in the code block below.
$form = $this->createFormBuilder(new User)
... //other add entries
->add('user_roles', EntityType::class, array(
'label' => 'Group (role)',
'class' => Role::class,
'choice_label' => 'name',
// 'mapped' => false, // Form works when false, but doesn't save/create UserRole entry
))
->getForm();
Hope this helps,
Cheers..
The general way you have chosen is okay. Stick with the EntityType and remove the mapped = false, this would tell Symfony to ignore the field.
I guess the problem is: you have a mixture of $this->user_role and $this->user_roles in your class, probably a renamed variable. Clean this up first in __construct(), addUserRole(), removeUserRole(), getUserRoles(), getUserRole().
Then add a method
public function setUserRoles($userRoles)
{
$this->user_roles = new ArrayCollection();
foreach ($userRoles as $role) {
$this->addUserRole($role);
}
return $this;
}

Categories