I'm currently trying out API Platform & I'm currently having a problem with my Data Persister when I try to create (post) a new user.
This is my User entity :
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Carbon\Carbon;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Annotation\SerializedName;
/**
* #ApiResource(
* collectionOperations={
* "get",
* "post"={
* "security"="is_granted('ROLE_ADMIN')"
* },
* },
* itemOperations={
* "get",
* "put"={"security"="is_granted('ROLE_ADMIN') and object == user"},
* "delete"={"security"="is_granted('ROLE_ADMIN')"}
* },
* attributes={
* "fetchEager": false,
* "normalization_context"={"groups"={"user.read"}},
* "denormalization_context"={"groups"={"user.write"}}
* }
* )
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\HasLifecycleCallbacks()
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({
* "user.read"
* })
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
* #Groups({
* "user.read",
* "user.write"
* })
*/
private $email;
/**
* #ORM\Column(type="json")
* #Groups({
* "user.read",
* "user.write"
* })
*/
private $roles = [];
/**
* #Groups({
* "user.write"
* })
*/
private $plainPassword;
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\Column(type="datetime")
*/
private $updatedAt;
/**
* User constructor.
*/
public function __construct()
{
$this->roles = [];
}
public function getId(): ?int
{
return $this->id;
}
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;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
/**
* #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 getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
/**
* #ORM\PrePersist()
*/
public function setCreatedAt(): void
{
$this->createdAt = Carbon::now();
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function setUpdatedAt(): void
{
$this->updatedAt = Carbon::now();
}
}
And here's my Data Persister :
<?php
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserDataPersister implements DataPersisterInterface
{
private $userPasswordEncoder;
private $entityManager;
public function __construct(EntityManagerInterface $entityManager, UserPasswordEncoderInterface $userPasswordEncoder)
{
$this->userPasswordEncoder = $userPasswordEncoder;
$this->entityManager = $entityManager;
}
public function supports($data): bool
{
return $data instanceof User;
}
/**
* #param User $data
*/
public function persist($data)
{
if ($data->getPlainPassword()) {
$data->setPassword(
$this->userPasswordEncoder->encodePassword($data, $data->getPlainPassword())
);
}
$this->entityManager->persist($data);
$this->entityManager->flush();
}
public function remove($data)
{
$this->entityManager->remove($data);
$this->entityManager->flush();
}
}
When I try to update (put) a user or delete (delete) one, my Data Persister does it's job correctly.
However, when I try to create a new user, I get this error :
"password: this value should not be null."
Also, here's the content of my request
{"email":"new.user#domain.com","plainPassword":"abcdefg","password":"abcdefg","roles":["ROLE_USER"]}
What am I doing wrong?
Thank you !
Related
how can i get doctrine in entity to get permission list?
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Role\Role;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
* #ORM\HasLifecycleCallbacks()
* #ORM\Table(name="`user`")
*/
class User implements UserInterface
{
private $entityManager;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $discordId;
/**
* #ORM\Column(type="string", length=180)
*/
private $username;
/**
* #ORM\Column(type="string", length=255, unique=true)
*/
private $email;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #ORM\OneToMany(targetEntity=Sites::class, mappedBy="owner", orphanRemoval=true)
*/
private $sites;
/**
* #ORM\PostLoad
* #ORM\PostPersist
*/
public function fetchEntityManager(LifecycleEventArgs $args)
{
$this->entityManager = $args->getEntityManager();
}
public function __construct()
{
$this->sites = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getdiscordId(): ?int
{
return $this->discordId;
}
public function setdiscordId(string $discordId): self
{
$this->discordId = $discordId;
return $this;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
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 getUserIdentifier(): string
{
return (string) $this->username;
}
public function getRoles() :array
{
// get permissions from group
$groupPermissionsRepo = $this->entityManager->getRepository(GroupPermissions::class);
$groupAccessList = $groupPermissionsRepo->getPermissionsForGroup($this->getGroupId());
// create access
$accessList = [];
foreach ($groupAccessList as $access) {
$accessList[] = new Role('ROLE_'.$access);
}
// set default role when group didn't have any access granted
if(count($groupAccessList) == 0)
$accessList[] = new Role('ROLE_USER');
// return access
return $accessList;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
/**
* #return Collection<int, Sites>
*/
public function getSites(): Collection
{
return $this->sites;
}
public function addSite(Sites $site): self
{
if (!$this->sites->contains($site)) {
$this->sites[] = $site;
$site->setOwner($this);
}
return $this;
}
public function removeSite(Sites $site): self
{
if ($this->sites->removeElement($site)) {
// set the owning side to null (unless already changed)
if ($site->getOwner() === $this) {
$site->setOwner(null);
}
}
return $this;
}
}
While using this code i get only error:
App\Entity\User::fetchEntityManager(): Argument #1 ($args) must be of type Doctrine\Common\Persistence\Event\LifecycleEventArgs, Doctrine\ORM\Event\LifecycleEventArgs given, called in C:\xampp\htdocs\dbshop2\vendor\doctrine\orm\lib\Doctrine\ORM\Event\ListenersInvoker.php on line 84
So here is my concern. I have a "Child" entity that contains $services and $users linked in ManyToMany.
The goal in my application is to be able to assign a child to a user of my service.
So, depending on the service I'm in, I won't have the same choice of users to assign.
In my ChildType, in my "users" field, I added a key ''query_builder'' that will retrieve only the users concerned.
My ChildType.php
<?php
namespace App\Form;
use App\Entity\Child;
use App\Entity\Service;
use App\Entity\Sexe;
use App\Entity\User;
use App\Repository\ServiceRepository;
use App\Repository\UserRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Security;
class ChildType extends AbstractType
{
/** #var User $user */
private $user;
private $serviceRepository;
private $authorization;
public function __construct(Security $security, AuthorizationCheckerInterface $authorization, ServiceRepository $serviceRepository)
{
$this->user = $security->getUser();
$this->authorization = $authorization;
$this->serviceRepository = $serviceRepository;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** #var Service $service */
$service = $this->user->getService();
$builder
->add('nom', TextType::class)
->add('prenom', TextType::class)
->add('sexe', EntityType::class, [
'class' => Sexe::class,
])
->add('dateDeNaissance', DateType::class, [
'widget' => 'single_text'
])
->add('lieuDeNaissance', TextType::class)
->add('nationalite', TextType::class)
->add('dateElaborationPpe', DateType::class, [
'widget' => 'single_text'
]);
if ($this->authorization->isGranted('ROLE_ADMIN')) {
$builder->add('users', EntityType::class, [
'class' => User::class,
'label' => 'Affecter à un utilisateur',
'query_builder' => function (UserRepository $userRepository) use ($service) {
return $userRepository->findByCurrentService($service);
},
'multiple' => true,
'expanded' => true,
]);
$builder->add('services', EntityType::class, [
'class' => Service::class,
'label' => 'Affecter à un service',
'choices' => $this->serviceRepository->findByDepartement($service->getDepartement()),
'choice_attr' => function($key, $val, $index) use ($service) {
return([]);
return $key->getCategorie()->getId() === $service->getCategorie()->getId() ?
['disabled' => 'disabled'] :
[];
},
'multiple' => true,
'expanded' => true,
]);
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Child::class,
]);
}
}
The problem:
Let's assume that my Service #1 has several users from Service #1 to my child. When I assign in my Service #2 users from Service #2 to the child, I have my 2 new users from Service #2 in the database, but on the other hand, those from Service #1 have been deleted, which is not the desired behavior.
I've been looking for a topic for almost 2 hours, and I can't find an answer, so if it's a duplicate, really sorry.
edit method in my controller:
/**
* #Route("/{id}/edit", name="child_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Child $child): Response
{
$this->denyAccessUnlessGranted("edit", $child);
$form = $this->createForm(ChildType::class, $child);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('child_show', ['id' => $child->getId()]);
}
return $this->render('child/edit.html.twig', [
'child' => $child,
'form' => $form->createView(),
]);
}
User.php:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use App\Entity\Service;
use App\Traits\BlameableEntity;
use App\Traits\TimestampableEntity;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
*
*/
class User implements UserInterface, \Serializable
{
use BlameableEntity;
use TimestampableEntity;
/**
* #var int
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string")
* #Assert\NotBlank()
*/
private $fullName;
/**
* #var string
*
* #ORM\Column(type="string", unique=true)
* #Assert\NotBlank()
* #Assert\Length(min=2, max=50)
*/
private $username;
/**
* #var string
*
* #ORM\Column(type="string", unique=true)
* #Assert\Email()
*/
private $email;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $password;
/**
* #var array
*
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var \DateTime
*
* #ORM\Column(type="datetime", nullable=true)
*/
private $lastLogin;
/**
* #var \DateTime
*
* #ORM\Column(type="datetime", nullable=true)
*/
private $secondToLastLogin;
/**
* #ORM\ManyToOne(targetEntity="Service", inversedBy="users")
*/
private $service;
/**
* #ORM\ManyToMany(targetEntity="Child", mappedBy="users")
*/
private $children;
public function __construct()
{
$this->children = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getFullName(): ?string
{
return $this->fullName;
}
public function setFullName(string $fullName): self
{
$this->fullName = $fullName;
return $this;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* Returns the roles or permissions granted to the user for security.
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantees that a user always has at least one role for security
if (empty($roles)) {
$roles[] = 'ROLE_USER';
}
return array_unique($roles);
}
public function setRoles(array $roles): void
{
$this->roles = $roles;
}
/**
* Get the value of lastLogin
*
* #return \DateTime
*/
public function getLastLogin()
{
return $this->lastLogin;
}
/**
* Set the value of lastLogin
*
* #param \DateTime $lastLogin
*
* #return self
*/
public function setLastLogin(\DateTime $lastLogin)
{
$this->lastLogin = $lastLogin;
return $this;
}
/**
* Get the value of secondToLastLogin
*
* #return \DateTime
*/
public function getSecondToLastLogin()
{
return $this->secondToLastLogin;
}
/**
* Set the value of secondToLastLogin
*
* #param \DateTime $secondToLastLogin
*
* #return self
*/
public function setSecondToLastLogin(\DateTime $secondToLastLogin)
{
$this->secondToLastLogin = $secondToLastLogin;
return $this;
}
/**
*
* {#inheritdoc}
*/
public function getSalt(): ?string
{
return null;
}
/**
* Removes sensitive data from the user.
*
* {#inheritdoc}
*/
public function eraseCredentials(): void
{
// if you had a plainPassword property, you'd nullify it here
// $this->plainPassword = null;
}
/**
* {#inheritdoc}
*/
public function serialize(): string
{
// add $this->salt too if you don't use Bcrypt or Argon2i
return serialize([$this->id, $this->username, $this->password]);
}
/**
* {#inheritdoc}
*/
public function unserialize($serialized): void
{
// add $this->salt too if you don't use Bcrypt or Argon2i
[$this->id, $this->username, $this->password] = unserialize($serialized, ['allowed_classes' => false]);
}
/**
* Get the value of service
*/
public function getService()
{
return $this->service;
}
/**
* Set the value of service
*
* #return self
*/
public function setService($service)
{
$this->service = $service;
return $this;
}
public function hasService(): bool
{
return !empty($this->getService());
}
/**
* #return Collection|Child[]
*/
public function getChildren(): Collection
{
return $this->children;
}
public function addChild(Child $child): self
{
if (!$this->children->contains($child)) {
$this->children[] = $child;
$child->addUser($this);
}
return $this;
}
public function removeChild(Child $child): self
{
if ($this->children->removeElement($child)) {
$child->removeUser($this);
}
return $this;
}
public function __toString()
{
return $this->getFullName();
}
}
Service.php :
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\ServiceRepository;
use App\Entity\Categorie;
use App\Entity\Departement;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
/**
* #ORM\Entity(repositoryClass=ServiceRepository::class)
*/
class Service
{
public const ASE = 'ASE';
public const MDPH = 'MDPH';
public const E_N = 'Éducation Nationale';
/**
* #var int
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $nom;
/**
* #var Departement
*
* #ORM\ManyToOne(targetEntity="Departement")
* #ORM\JoinColumn(nullable=false)
*/
private $departement;
/**
* #var Categorie
*
* #ORM\ManyToOne(targetEntity="Categorie")
* #ORM\JoinColumn(nullable=false)
*/
private $categorie;
/**
* #ORM\OneToMany(targetEntity="User", mappedBy="service")
*/
private $users;
/**
* #ORM\ManyToMany(targetEntity="Child", mappedBy="services")
*/
private $children;
/**
* #ORM\ManyToMany(targetEntity="Element", mappedBy="services")
*/
private $elements;
public function __construct()
{
$this->users = new ArrayCollection();
$this->children = new ArrayCollection();
$this->elements = new ArrayCollection();
}
/**
* Get the value of id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set the value of id
*
* #param int $id
*
* #return self
*/
public function setId(int $id)
{
$this->id = $id;
return $this;
}
/**
* Get the value of nom
*
* #return string
*/
public function getNom()
{
return $this->nom;
}
/**
* Set the value of nom
*
* #param string $nom
*
* #return self
*/
public function setNom(string $nom)
{
$this->nom = $nom;
return $this;
}
/**
* Get the value of departement
*
* #return Departement
*/
public function getDepartement()
{
return $this->departement;
}
/**
* Set the value of departement
*
* #param Departement $departement
*
* #return self
*/
public function setDepartement(Departement $departement)
{
$this->departement = $departement;
return $this;
}
/**
* Get the value of categorie
*
* #return Categorie
*/
public function getCategorie()
{
return $this->categorie;
}
/**
* Set the value of categorie
*
* #param Categorie $categorie
*
* #return self
*/
public function setCategorie(Categorie $categorie)
{
$this->categorie = $categorie;
return $this;
}
/**
* #return Collection|Users[]
*/
public function getUsers(): Collection
{
return $this->users;
}
public function addUser(User $user): self
{
if (!$this->users->contains($user)) {
$this->users[] = $user;
$user->setService($this);
}
return $this;
}
public function removeUser(User $user): self
{
if ($this->users->removeElement($user)) {
// set the owning side to null (unless already changed)
if ($user->getService() === $this) {
$user->setService(null);
}
}
return $this;
}
/**
* #return Collection|Children[]
*/
public function getChildren(): Collection
{
return $this->children;
}
public function addChild(Child $child): self
{
if (!$this->children->contains($child)) {
$this->children[] = $child;
$child->addService($this);
}
return $this;
}
public function removeChild(Child $child): self
{
if ($this->children->removeElement($child)) {
$child->removeService($this);
}
return $this;
}
/**
* #return Collection|Elements[]
*/
public function getElements(): Collection
{
return $this->elements;
}
public function addElements(Element $element): self
{
if (!$this->children->contains($element)) {
$this->elements[] = $element;
$element->addService($this);
}
return $this;
}
public function removeElement(Element $element): self
{
if ($this->elements->removeElement($element)) {
$element->removeService($this);
}
return $this;
}
public function __toString()
{
return $this->getNom();
}
}
Child.php :
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Service;
use App\Entity\Sexe;
use App\Entity\User;
use DateTime;
use App\Repository\ChildRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use App\Traits\TimestampableEntity;
use App\Traits\BlameableEntity;
/**
* #ORM\Entity(repositoryClass=ChildRepository::class)
*/
class Child
{
use BlameableEntity;
use TimestampableEntity;
/**
* #var int
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $nom;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $prenom;
/**
* #var Sexe
*
* #ORM\ManyToOne(targetEntity="Sexe")
*/
private $sexe;
/**
* #var date
*
* #ORM\Column(type="date")
*/
private $dateDeNaissance;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $lieuDeNaissance;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $nationalite;
/**
* #var date
*
* #ORM\Column(type="datetime", nullable=true)
*/
private $dateElaborationPpe;
/**
* #var Service[]|Collection
*
* #ORM\ManyToMany(targetEntity="Service", inversedBy="children")
*/
private $services;
/**
* #var User[]|Collection
*
* #ORM\ManyToMany(targetEntity="User", inversedBy="children")
*/
private $users;
/**
* #var Element[]|Collection
*
* #ORM\OneToMany(targetEntity="Element", mappedBy="child")
*/
private $elements;
public function __construct()
{
$this->services = new ArrayCollection();
$this->users = new ArrayCollection();
$this->elements = new ArrayCollection();
}
/**
* Get the value of id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set the value of id
*
* #param int $id
*
* #return self
*/
public function setId(int $id)
{
$this->id = $id;
return $this;
}
/**
* Get id
*
* #return string
*/
public function getNumDossier()
{
return str_pad($this->id, 6, "0", STR_PAD_LEFT);
}
/**
* Get the value of nom
*/
public function getNom()
{
return $this->nom;
}
/**
* Set the value of nom
*
* #return self
*/
public function setNom(string $nom)
{
$this->nom = $nom;
return $this;
}
/**
* Get the value of prenom
*/
public function getPrenom()
{
return $this->prenom;
}
/**
* Set the value of prenom
*
* #return self
*/
public function setPrenom(string $prenom)
{
$this->prenom = $prenom;
return $this;
}
/**
* Get the value of sexe
*
* #return Sexe
*/
public function getSexe()
{
return $this->sexe;
}
/**
* Set the value of sexe
*
* #param Sexe $sexe
*
* #return self
*/
public function setSexe(Sexe $sexe)
{
$this->sexe = $sexe;
return $this;
}
/**
* Get the value of dateDeNaissance
*/
public function getDateDeNaissance()
{
return $this->dateDeNaissance;
}
/**
* Set the value of dateDeNaissance
*
* #return self
*/
public function setDateDeNaissance(datetime $dateDeNaissance)
{
$this->dateDeNaissance = $dateDeNaissance;
return $this;
}
/**
* Get the value of lieuDeNaissance
*/
public function getLieuDeNaissance()
{
return $this->lieuDeNaissance;
}
/**
* Set the value of lieuDeNaissance
*
* #return self
*/
public function setLieuDeNaissance(string $lieuDeNaissance)
{
$this->lieuDeNaissance = $lieuDeNaissance;
return $this;
}
/**
* Get the value of nationalite
*/
public function getNationalite()
{
return $this->nationalite;
}
/**
* Set the value of nationalite
*
* #return self
*/
public function setNationalite(string $nationalite)
{
$this->nationalite = $nationalite;
return $this;
}
/**
* Get the value of dateElaborationPpe
*/
public function getDateElaborationPpe()
{
return $this->dateElaborationPpe;
}
/**
* Set the value of dateElaborationPpe
*
* #return self
*/
public function setDateElaborationPpe(datetime $dateElaborationPpe)
{
$this->dateElaborationPpe = $dateElaborationPpe;
return $this;
}
/**
* #return Collection|Service[]
*/
public function getServices(): Collection
{
return $this->services;
}
public function addService(Service $service): self
{
if (!$this->services->contains($service)) {
$this->services[] = $service;
}
return $this;
}
public function removeService(Service $service): self
{
$this->services->removeElement($service);
return $this;
}
/**
* #return Collection|User[]
*/
public function getUsers(): Collection
{
return $this->users;
}
public function addUser(User $user): self
{
$test = 'test';
if (!$this->users->contains($user)) {
$this->users[] = $user;
}
return $this;
}
public function removeUser(User $user): self
{
$this->users->removeElement($user);
return $this;
}
public function getNbUsers()
{
return count($this->getUsers());
}
/**
* #return Collection|Element[]
*/
public function getElements(): Collection
{
return $this->elements;
}
public function addElement(Element $element): self
{
if (!$this->elements->contains($element)) {
$this->elements[] = $element;
}
return $this;
}
public function removeElement(Element $element): self
{
$this->elements->removeElement($element);
return $this;
}
public function isNotAssigned(): bool
{
return (!$this->getNbUsers());
}
public function __toString(): string
{
return $this->getNom() . ' ' . $this->getPrenom();
}
}
Thanks in advance for your help.
I have this custom API endpoint call "register". I had added into "ItemOperations" of the User Entity with the declaration of "POST". When I check back to api platform UI, I saw that there is unnecessary ID field is being mandatory to key in as shown in the screenshot attached.
This is my User Entity codes that I have tried
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ApiResource(
* collectionOperations={"get","post"},
* itemOperations={
* "get",
* "put",
* "register"={
* "method"="POST",
* "path"="/register",
* "controller"=User::class,
* }
* },
* normalizationContext={
* "groups"={"user:read"},"swagger_definition_name"="Read"
* },
* denormalizationContext={
* "groups"={"user:write"},"swagger_definition_name"="Write"
* },
* shortName="User"
*
* )
* #UniqueEntity(fields={"email"})
* #UniqueEntity(fields={"contact"})
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
* #Groups({"user:read", "user:write"})
* #Assert\Email()
* #Assert\NotBlank()
*/
private $email;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
* #Groups({"user:write"})
* #Assert\NotBlank()
*/
private $password;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"user:read", "user:write"})
* #Assert\NotBlank()
*/
private $firstName;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"user:read", "user:write"})
* #Assert\NotBlank()
*/
private $lastName;
/**
* #var string provide in YYYY-MM-DD (neglect Time)
* #ORM\Column(type="date")
* #Groups({"user:read", "user:write"})
* #Assert\NotBlank()
*/
private $dob;
/**
* #ORM\Column(type="text")
* #Groups({"user:read", "user:write"})
* #Assert\NotBlank()
*/
private $address;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"user:read", "user:write"})
* #Assert\NotBlank()
* #Assert\Length(
* min=8,
* max=8,
* maxMessage="contact number must have 8 character",
* minMessage="contact number must have 8 character"
* )
*/
private $contact;
public function getId(): ?int
{
return $this->id;
}
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->firstName;
}
public function setFirstName(string $firstName): self
{
$this->firstName = $firstName;
return $this;
}
public function getLastName(): ?string
{
return $this->lastName;
}
public function setLastName(string $lastName): self
{
$this->lastName = $lastName;
return $this;
}
public function getDob(): ?\DateTimeInterface
{
return $this->dob;
}
public function setDob(\DateTimeInterface $dob): self
{
$this->dob = $dob;
return $this;
}
public function getAddress(): ?string
{
return $this->address;
}
public function setAddress(string $address): self
{
$this->address = $address;
return $this;
}
public function getContact(): ?string
{
return $this->contact;
}
public function setContact(string $contact): self
{
$this->contact = $contact;
return $this;
}
}
And this is my AuthController
<?php
namespace App\Controller;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class AuthController extends AbstractController
{
/**
* #param Request $request
* #param UserPasswordEncoderInterface $encoder
* #param EntityManagerInterface $entity_manager
*
* #return JsonResponse
* #throws \Exception
*/
public function register(Request $request, UserPasswordEncoderInterface $encoder, EntityManagerInterface $entity_manager)
{
try{
$contentType = $request->getContentType();
$content = $request->getContent();
$response = new JsonResponse();
if ($contentType != 'json' || !$content) {
$response -> setContent(json_encode(['fail' => 'empty content type or content type is not json format']));
$response ->setStatusCode(Response::HTTP_BAD_REQUEST,'Bad Request');
}else{
$data = json_decode($content, true);
$email = $data['email'];
$hasUser = $entity_manager->getRepository(User::class)->findByEmail($email);
if($hasUser){
$response -> setContent(json_encode(['fail' => 'user already registered']));
$response ->setStatusCode(Response::HTTP_BAD_REQUEST,'Bad Request');
return $response;
}
//Update User
$user = new User();
$user ->setEmail($data['email']);
$user ->setFirstName($data['firstName']);
$user->setLastName($data['lastName']);
$user->setDob(new \DateTime($data['dob']));
$user->setAddress($data['address']);
$user->setContact($data['contact']);
$user->setPassword($encoder->encodePassword($user,
$data['password']
));
$entity_manager->persist($user);
$entity_manager->flush();
$response -> setContent(json_encode(['success' => 'user successfully added']));
$response -> setStatusCode(Response::HTTP_CREATED,'Created');
}
return $response;
}catch (Exception $e){
//TODO: Log Error
$response -> setContent(json_encode(['fail' => 'Internal Server Error']));
$response ->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR,'Bad Request');
}
}
}
I wanted to remove the "Id" mandatory field when I post register. Is there any way that I can do? I am stuck this problem for almost two days. Sorry if I miss out something since I am quite new to API platform
You need to use the POST Action in Collection Operations, ItemOperation are for getting, updating or deleting your resource, thats why the id is mandatory.
And point your Action to the controller, not the Entity
#ApiResource(
* collectionOperations={
* "get",
* "post",
* "register"={
* "method"="POST",
* "path"="/register",
* "controller"= AuthController::class,
* }
* },
* itemOperations={
* "get",
* "put",
* },
* normalizationContext={
* "groups"={"user:read"},"swagger_definition_name"="Read"
* },
* denormalizationContext={
* "groups"={"user:write"},"swagger_definition_name"="Write"
* },
* shortName="User"
* * )
And Sorry I'm French :)
For my diploma I have to make a Tickets/Reports app and I'm stuck at the Doctrine mapping.
Clone project on GitHUB
Here is my 'Transaction.php' class:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\TransactionRepository")
*/
class Transaction
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
************************** PROBLEM STARTS HERE *************************
/**
* #ORM\OneToOne(targetEntity="User")
*/
private $sender;
/**
* #ORM\OneToOne(targetEntity="User")
*/
private $receiver;
************************** PROBLEM ENDS HERE *************************
/**
* #ORM\Column(type="integer")
*/
private $amount;
/**
* #ORM\Column(type="datetime",nullable=true)
*/
private $date;
/**
* #ORM\Column(type="text")
*/
private $comment;
/**
* #return mixed
*/
public function getComment()
{
return $this->comment;
}
/**
* #param mixed $comment
*/
public function setComment($comment): void
{
$this->comment = $comment;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return mixed
*/
public function getSender()
{
return $this->sender;
}
/**
* #param mixed $sender
*/
public function setSender($sender): void
{
$this->sender = $sender;
}
/**
* #return mixed
*/
public function getReceiver()
{
return $this->receiver;
}
/**
* #param mixed $receiver
*/
public function setReceiver($receiver): void
{
$this->receiver = $receiver;
}
/**
* #return mixed
*/
public function getAmount()
{
return $this->amount;
}
/**
* #param mixed $amount
*/
public function setAmount($amount): void
{
$this->amount = $amount;
}
/**
* #return mixed
*/
public function getDate()
{
return $this->date;
}
/**
* #param mixed
*/
public function setDate()
{
$this->date = new \DateTime("now");
}
}
And my 'User.php' class
<?php
// src/Entity/User.php
namespace App\Entity;
use App\Repository\TransactionRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #UniqueEntity(fields="email", message="Email already taken")
* #UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Assert\NotBlank()
* #Assert\Email()
*/
private $email;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Assert\NotBlank()
*/
private $username;
/**
* #Assert\NotBlank()
* #Assert\Length(max=4096)
*/
private $plainPassword;
/**
* The below length depends on the "algorithm" you use for encoding
* the password, but this works well with bcrypt.
*
* #ORM\Column(type="string", length=64)
*/
private $password;
/**
* #var Monycks
*/
private $monycks = 10000;
/**
* #var Skill
* #ORM\ManyToOne(targetEntity="Skill",inversedBy="users")
*/
private $skill;
/**
* #return Skill
*/
public function getSkill()
{
return $this->skill;
}
/**
* #param mixed $skill
*/
public function setSkill(Skill $skill): void
{
$this->skill = $skill;
$skill->addUser($this);
}
/**
* #return Monycks
*/
public function getMonycks()
{
return $this->monycks;
}
/**
* #param Monycks $monycks
*/
public function setMonycks($monycks): void
{
$this->monycks = $monycks;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
// other properties and methods
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getSalt()
{
// The bcrypt algorithm doesn't require a separate salt.
// You *may* need a real salt if you choose a different encoder.
return null;
}
public function getRoles()
{
if($this->getUsername()=='admin')
return array('ROLE_ADMIN', 'ROLE_USER');
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt,
));
}
/** #see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt
) = unserialize($serialized);
}
public function isSender()
{
return $this->getId();
}
public function isReceiver()
{
return $this->getId();
}
}
My problem is that Users can have multiple Transactions as sender or receiver an Transaction have at least 2 user (Send & receive)
With this config, I can add one transaction for each user....
I still do not manage very well Doctrine relation yet...
So somedy can EXPLAIN me how to do the tricks and how it works....
Clone project on GitHUB
I finaly found a workinf solution but I don't now if it's the right way to do
it...
I make a contrustor with two ArrayCollection(); in my User.php class and I put users_id into.
And in my Transaction.php class #ORM\JoinColumn
In my 'Transaction.php' class I put:
Everything is working now, but I don't really understand what's appened...
Someone can explain me correctly what was the problem .??
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="senders")
* #ORM\JoinColumn(nullable=true)
*/
private $sender;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="receivers")
* #ORM\JoinColumn(nullable=true)
*/
private $receiver;
public function getSender()
{
return $this->sender;
}
public function setSender(User $user)
{
$this->sender = $user;
}
public function getReceiver()
{
return $this->receiver;
}
public function setReceiver(User $user)
{
$this->receiver = $user;
}
And in my 'User.php' class:
/**
* #ORM\OneToMany(targetEntity="Transaction", mappedBy="receiver")
*/
private $receivers;
/**
* #ORM\OneToMany(targetEntity="Transaction", mappedBy="receiver")
*/
private $receivers;
/**
* #ORM\OneToMany(targetEntity="Transaction", mappedBy="sender")
*/
private $senders;
public function __construct()
{
$this->senders = new ArrayCollection();
$this->receivers = new ArrayCollection();
}
/**
* #return Collection|Transaction[]
*/
public function getReceivers()
{
return $this->receivers;
}
/**
* #return Collection|Transaction[]
*/
public function getSenders()
{
return $this->senders;
}
i have a big problem implementing JWT Tokens on symfony.
I already make work the JWT token, but i need to add to the token info the User roles too. i am doing this using a Listener (JWTCreatedListener):
public function onJWTCreated(JWTCreatedEvent $event)
{
$request = $this->requestStack->getCurrentRequest();
$payload = $event->getData();
$payload['ip'] = $request->getClientIp();
$payload['roles'] = $event->getUser()->getRoles();
$event->setData($payload);
}
I implemented the Role.php (AppBundle/Entity/Role.php) on this way:
<?php
namespace AppBundle\Entity;
use Symfony\Component\Security\Core\Role\RoleInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="acme_role")
* #ORM\Entity()
*/
class Role implements RoleInterface
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=30)
*/
private $name;
/**
* #ORM\Column(name="role", type="string", length=20, unique=true)
*/
private $role;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="roles")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* #see RoleInterface
*/
public function getRole()
{
return $this->role;
}
// ... getters and setters for each property
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Role
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set role
*
* #param string $role
*
* #return Role
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* Add user
*
* #param \AppBundle\Entity\User $user
*
* #return Role
*/
public function addUser(\AppBundle\Entity\User $user)
{
$this->users[] = $user;
return $this;
}
/**
* Remove user
*
* #param \AppBundle\Entity\User $user
*/
public function removeUser(\AppBundle\Entity\User $user)
{
$this->users->removeElement($user);
}
/**
* Get users
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getUsers()
{
return $this->users;
}
}
And my User class:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Table(name="users")
* #ORM\Entity
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=25, unique=true)
*/
private $username;
/**
* #ORM\Column(type="string", length=500)
*/
private $password;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
*
*/
private $roles;
public function __construct($username)
{
$this->isActive = true;
$this->username = $username;
$this->roles = new ArrayCollection();
}
public function getUsername()
{
return $this->username;
}
public function getSalt()
{
return null;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getRoles()
{
return $this->roles->toArray();
}
public function eraseCredentials()
{
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* #param string $username
*
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Set isActive
*
* #param boolean $isActive
*
* #return User
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
/**
* Add role
*
* #param \AppBundle\Entity\Role $role
*
* #return User
*/
public function addRole(\AppBundle\Entity\Role $role)
{
$this->roles[] = $role;
return $this;
}
/**
* Remove role
*
* #param \AppBundle\Entity\Role $role
*/
public function removeRole(\AppBundle\Entity\Role $role)
{
$this->roles->removeElement($role);
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return $this->isActive;
}
// serialize and unserialize must be updated - see below
public function serialize()
{
return serialize(array(
// ...
$this->isActive
));
}
public function unserialize($serialized)
{
list (
// ...
$this->isActive
) = unserialize($serialized);
}
}
The problem is that this method getRole() always returns empty.
This is my db data:
[users]
id username password is_active
1 abriceno $2y$13$NW6uNOKJGUQTSXirej4HKOwIa6mWzYqFxzz1ppWQjyp... 1
[acme_role]
id name role
1 admin ROLE_ADMIN
[user_role]
user_id user_role
1 1
Also, i try to call the data from a controller test using doctrine:
public function indexAction(Request $request)
{
$repository = $this->getDoctrine()->getRepository('AppBundle:User');
$user = $repository->findOneByusername('abriceno');
$username = $user->getUsername();
$roles = $user->getRoles();
$arr = array(
'username' => $user->getUsername(),
'password' => $user->getPassword(),
'roles' => $user->getRoles()
);
return new JsonResponse($arr);
this returns:
{"username":"abriceno","password":"$2y$13$NW6uNOKJGUQTSXirej4HKOwIa6mWzYqFxzz1ppWQjypQJLIgUGJ.m","roles":[{}]}
I am so desperate... thanks for all the help that you can provide to me.
UPDATE 1
If i do print_r($role) this prints a huuuuuge list of values:
array(1) { [0]=> object(AppBundle\Entity\Role)#624 (4) { ["id":"AppBundle\Entity\Role":private]=> int(1) ["name":"AppBundle\Entity\Role":private]=> string(5) "admin" ["role":"AppBundle\Entity\Role":private]=> string(10) "ROLE_ADMIN" ["users":"AppBundle\Entity\Role":private]=> object(Doctrine\ORM\PersistentCollection)#626 (9) { ["snapshot":"Doctrine\ORM\PersistentCollection":private]=> array(0) { } ["owner":"Doctrine\ORM\PersistentCollection":private]=> *RECURSION*
... and keeps going... very strange!!
Seems that the join table is not handled directly by doctrine, so you need do specify to doctrine howto map the referenced fied with the JoinTable annotation (See the default mapping section.)
In your case, try to modify the roles relation definition in the User entity as follow:
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
* #ORM\JoinTable(name="user_role",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_role", referencedColumnName="id")})
*
*/
private $roles;
Hope this help
Finally i fix this with this code:
// Work of roles
$roles = $event->getUser()->getRoles();
$role_length = count($roles);
$role_list = array();
for ($i=0; $i <$role_length ; $i++) {
array_push($role_list,$roles[$i]->getRole());
}
$payload = $event->getData();
$payload['ip'] = $request->getClientIp();
$payload['roles'] = $role_list;
The problem (i guess) is on the ->getRoles(); code. This returns a array of Entity\Role class, not an array of roles.
Now the dump is:
{
"token": "eyJhbGciOiJSUzI1NiJ9.....",
"data": {
"roles": [
"ROLE_ADMIN"
]
}
}