I am currently using EasyAdmin 4 with Symfony to create a backend admin page to manage users.
I did all the step that was in the documentation but yet it still giving me an error:
"Unable to find the controller related to the "App\Controller\Admin\User" Entity; did you forget to extend "EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController"?"
Here is the code in DashboardController:
<?php
namespace App\Controller\Admin;
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DashboardController extends AbstractDashboardController
{
#[Route('/admin', name: 'admin')]
public function index(): Response
{
return $this->render('admin/dashboard.html.twig');
}
public function configureDashboard(): Dashboard
{
return Dashboard::new()
->setTitle('Symfony Melody')
->renderContentMaximized();
}
public function configureMenuItems(): iterable
{
yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home');
yield MenuItem::linkToCrud('User', 'fas fa-user', User::class);
}
}
Here is the code in UserCrudController:
<?php
namespace App\Controller\Admin;
use App\Entity\User;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
class UserCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return User::class;
}
public function configureCrud(Crud $crud): Crud
{
return $crud;
}
/*
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id'),
TextField::new('title'),
TextEditorField::new('description'),
];
}
*/
}
Here is the code in the User class:
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[UniqueEntity(fields: ['email'], message: 'There is already an account with this email')]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
/**
* #var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
#[ORM\Column(length: 255)]
private ?string $nom = null;
#[ORM\Column(length: 255)]
private ?string $prenom = null;
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 getUserIdentifier(): 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 PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getPrenom(): ?string
{
return $this->prenom;
}
public function setPrenom(string $prenom): self
{
$this->prenom = $prenom;
return $this;
}
}
I tried reading the documentation again but i can't find any answers.
Can someone help please? it would be great, thank you.
You are Missing the use of App\Entity\User
In your DashboardController try to add this line :
use App\Entity\User;
On the top of your class file
Happy coding 🙂
Related
I'm new to symfony, I work on an e-commerce website. I solved some bugs already trying different solutions but with this one I feel stuck. I'm trying to display my articles by categories but it's not working because it displays an error:
Warning: Trying to access array offset on value of type null
I made a consoles controller in symfony to call the view associated to display only articles with that category, expected to do the same with the games after but it doesn't seem to find the category associated with the articles ?
This is my consolescontroller:
namespace App\Controller;
use App\Repository\ArticlesRepository;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class ConsolesController extends AbstractController
{
#[Route('/consoles', name: 'app_consoles')]
public function index(ArticlesRepository $repositoryArticles): Response
{
$consoles = $repositoryArticles->findBy(['category' => 1]);
return $this->render('consoles/index.html.twig', [
'controller_name' => 'ConsolesController',
'consoles' => $consoles,
]);
}
}
My Entity Articles
<?php
namespace App\Entity;
use App\Repository\ArticlesRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ArticlesRepository::class)]
class Articles
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 80)]
private ?string $nomArticle = null;
#[ORM\Column(length: 255)]
private ?string $imageArticle = null;
#[ORM\Column]
private ?float $prixArticle = null;
#[ORM\Column(length: 200)]
private ?string $descriptionArticle = null;
#[ORM\ManyToMany(targetEntity: Categories::class, inversedBy: 'articles')]
#[ORM\JoinColumn(nullable: false)]
private Collection $category;
public function __construct()
{
$this->category = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNomArticle(): ?string
{
return $this->nomArticle;
}
public function setNomArticle(string $nomArticle): self
{
$this->nomArticle = $nomArticle;
return $this;
}
public function getImageArticle(): ?string
{
return $this->imageArticle;
}
public function setImageArticle(string $imageArticle): self
{
$this->imageArticle = $imageArticle;
return $this;
}
public function getPrixArticle(): ?float
{
return $this->prixArticle;
}
public function setPrixArticle(float $prixArticle): self
{
$this->prixArticle = $prixArticle;
return $this;
}
public function getDescriptionArticle(): ?string
{
return $this->descriptionArticle;
}
public function setDescriptionArticle(string $descriptionArticle): self
{
$this->descriptionArticle = $descriptionArticle;
return $this;
}
/**
* #return Collection<int, Categories>
*/
public function getCategory(): Collection
{
return $this->category;
}
public function addCategory(Categories $category): self
{
if (!$this->category->contains($category)) {
$this->category->add($category);
}
return $this;
}
public function removeCategory(Categories $category): self
{
$this->category->removeElement($category);
return $this;
}
public function __toString()
{
return $this->nomArticle;
}
}
And my Entity Categories
<?php
namespace App\Entity;
use App\Repository\CategoriesRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: CategoriesRepository::class)]
class Categories
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 50)]
private ?string $nomCategorie = null;
#[ORM\ManyToMany(targetEntity: Articles::class, mappedBy: 'category')]
private Collection $articles;
public function __construct()
{
$this->articles = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getNomCategorie(): ?string
{
return $this->nomCategorie;
}
public function setNomCategorie(string $nomCategorie): self
{
$this->nomCategorie = $nomCategorie;
return $this;
}
/**
* #return Collection<int, Articles>
*/
public function getArticles(): Collection
{
return $this->articles;
}
public function addArticle(Articles $article): self
{
if (!$this->articles->contains($article)) {
$this->articles->add($article);
$article->addCategory($this);
}
return $this;
}
public function removeArticle(Articles $article): self
{
if ($this->articles->removeElement($article)) {
$article->removeCategory($this);
}
return $this;
}
public function __toString()
{
return $this->nomCategorie;
}
}
They're both linked by a "many to many" relationship and they're both supposed to have access to the other but there still something not working.
Thank you in advance for your help.
I would like to understand dql logic and i would like to create this kind of request.
Bellow this is the SQL request i would like to do :
SELECT titlemission,descriptionmission,onsetdate,deadline,budgetmission,prioritymission,company FROM mission
left join mission_tag mt on mission.id = mt.mission_id
left join tag on mt.tag_id = tag.id
left join user on user.id = mission.iduser_id
where nomtag = "test20";
I read a lot of documentation but no one explain exatly how to use this QueryBuilder.
Symfony documentation is too abstract for me.
I only know that you have to forget the way you edit the sql request and think in object and entity relation. But i don't know how to do that.
This is my entity Tag
<?php
namespace App\Entity;
use App\Repository\TagRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: TagRepository::class)]
class Tag
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 100)]
#[Assert\NotBlank]
#[Assert\Length(min:2)]
#[Assert\Valid]
private ?string $nomtag = null;
#[ORM\ManyToMany(targetEntity: Mission::class, mappedBy: 'tags', cascade: ['persist'])]
#[ORM\JoinTable(name: 'mission_tag')]
#[ORM\JoinColumn(name: 'tag_id',referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name:'mission_id', referencedColumnName: 'id')] // Mission_id est couplé à l'id de mission
private Collection $missions;
public function __construct()
{
$this->missions = new ArrayCollection();
}
// #[ORM\ManyToMany(targetEntity: Mission::class, inversedBy:"$idtagmissionassign")]
// private $genusScientists;
public function getId(): ?int
{
return $this->id;
}
public function getNomtag(): ?string
{
return $this->nomtag;
}
public function setNomtag(string $nomtag): self
{
$this->nomtag = $nomtag;
return $this;
}
/**
* #return Collection<int, Mission>
*/
public function getMissions(): Collection
{
return $this->missionstag;
}
public function addMission(Mission $missions): self
{
if (!$this->missions->contains($missions)) {
$this->missions->add($missions);
$missions->addTag($this);
}
return $this;
}
public function removeMission(Mission $missions): self
{
if ($this->missions->removeElement($missions)) {
$missions->removeTag($this);
}
return $this;
}
public function __toString(): string
{
return $this->nomtag;
}
}
This is my entity user :
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: 'user')]
#[UniqueEntity(fields: ['email'], message: 'Un compte existe déjà avec cette adresse email')]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
/**
* #var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
#[ORM\Column(length: 100)]
private ?string $name = null;
#[ORM\Column(length: 100)]
private ?string $firstname = null;
#[ORM\Column(length: 150)]
private ?string $company = null;
#[ORM\Column(type: 'boolean')]
private $isVerified = false;
#[ORM\Column(length: 255)]
private ?string $companyadress = null;
#[ORM\OneToMany(mappedBy: 'iduser', targetEntity: Mission::class)]
private Collection $missions;
#[ORM\OneToMany(mappedBy: 'iduser', targetEntity: Service::class)]
private Collection $services;
#[ORM\ManyToMany(targetEntity: Service::class, mappedBy: 'idserviceassign')]
private Collection $servicesassign;
#[ORM\ManyToMany(targetEntity: Mission::class, mappedBy: 'idmissionassign')]
#[ORM\JoinTable(name: 'mission_user')]
#[ORM\JoinColumn(name: 'user_id',referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name:'mission_id', referencedColumnName: 'id')]
private Collection $missionsassign;
public function __construct()
{
$this->missions = new ArrayCollection();
$this->services = new ArrayCollection();
$this->servicesassign = new ArrayCollection();
$this->missionsassign = new ArrayCollection();
}
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 getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'USER_DISCOVER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getFirstname(): ?string
{
return $this->firstname;
}
public function setFirstname(string $firstname): self
{
$this->firstname = $firstname;
return $this;
}
public function getCompany(): ?string
{
return $this->company;
}
public function setCompany(string $company): self
{
$this->company = $company;
return $this;
}
public function isVerified(): bool
{
return $this->isVerified;
}
public function setIsVerified(bool $isVerified): self
{
$this->isVerified = $isVerified;
return $this;
}
public function getCompanyadress(): ?string
{
return $this->companyadress;
}
public function setCompanyadress(string $companyadress): self
{
$this->companyadress = $companyadress;
return $this;
}
public function __toString(){
return $this->name;
}
/**
* #return Collection<int, Mission>
*/
public function getMissions(): Collection
{
return $this->missions;
}
public function addMission(Mission $missions): self
{
if (!$this->missions->contains($missions)) {
$this->missions->add($missions);
$missions->setIduser($this);
}
return $this;
}
public function removeMission(Mission $missions): self
{
if ($this->missions->removeElement($missions)) {
// set the owning side to null (unless already changed)
if ($missions->getIduser() === $this) {
$missions->setIduser(null);
}
}
return $this;
}
/**
* #return Collection<int, Service>
*/
public function getServices(): Collection
{
return $this->services;
}
public function addService(Service $service): self
{
if (!$this->services->contains($service)) {
$this->services->add($service);
$service->setIduser($this);
}
return $this;
}
public function removeService(Service $service): self
{
if ($this->services->removeElement($service)) {
// set the owning side to null (unless already changed)
if ($service->getIduser() === $this) {
$service->setIduser(null);
}
}
return $this;
}
/**
* #return Collection<int, Service>
*/
public function getServicesassign(): Collection
{
return $this->servicesassign;
}
public function addServicesassign(Service $servicesassign): self
{
if (!$this->servicesassign->contains($servicesassign)) {
$this->servicesassign->add($servicesassign);
$servicesassign->addIdserviceassign($this);
}
return $this;
}
public function removeServicesassign(Service $servicesassign): self
{
if ($this->servicesassign->removeElement($servicesassign)) {
$servicesassign->removeIdserviceassign($this);
}
return $this;
}
/**
* #return Collection<int, Mission>
*/
public function getMissionsassign(): Collection
{
return $this->missionsassign;
}
public function addMissionsassign(Mission $missionsassign): self
{
if (!$this->missionsassign->contains($missionsassign)) {
$this->missionsassign->add($missionsassign);
$missionsassign->addIdmissionassign($this);
}
return $this;
}
public function removeMissionsassign(Mission $missionsassign): self
{
if ($this->missionsassign->removeElement($missionsassign)) {
$missionsassign->removeIdmissionassign($this);
}
return $this;
}
}
And this is my entity Mission
<?php
namespace App\Entity;
use App\Repository\MissionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: MissionRepository::class)]
#[ORM\Table(name: 'mission')]
class Mission
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $titlemission = null;
#[ORM\Column(length: 255)]
private ?string $descriptionmission = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $onsetdate = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $deadline = null;
#[ORM\Column]
private ?int $budgetmission = null;
/*#[ORM\Column(length: 255)]
private ?string $codeapemission = null;*/
#[ORM\Column(length: 255)]
private ?string $prioritymission = null;
#[ORM\ManyToMany(targetEntity: Tag::class, inversedBy: 'missions', cascade: ['persist'])]
#[ORM\JoinTable(name: 'mission_tag')]
#[ORM\JoinColumn(name: 'mission_id',referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name:'tag_id', referencedColumnName: 'id')] //tag_id qui est couplé avec id de tag
private Collection $tags;
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'missions', cascade: ['persist'], fetch:"EAGER")]
#[ORM\JoinColumn(nullable: false)]
private User $iduser;
#[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'missionsassign')]
#[ORM\JoinTable(name: 'mission_user')]
#[ORM\JoinColumn(name: 'mission_id',referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name:'user_id', referencedColumnName: 'id')]
private Collection $idmissionassign;
#[ORM\Column(length: 100)]
private ?string $remote = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $datecreation = null;
public function __construct()
{
$this->tags = new ArrayCollection();
$this->idmissionassign = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitlemission(): ?string
{
return $this->titlemission;
}
public function setTitlemission(string $titlemission): self
{
$this->titlemission = $titlemission;
return $this;
}
public function getDescriptionmission(): ?string
{
return $this->descriptionmission;
}
public function setDescriptionmission(string $descriptionmission): self
{
$this->descriptionmission = $descriptionmission;
return $this;
}
public function getOnsetdate(): ?\DateTimeInterface
{
return $this->onsetdate;
}
public function setOnsetdate(\DateTimeInterface $onsetdate): self
{
$this->onsetdate = $onsetdate;
return $this;
}
public function getDeadline(): ?\DateTimeInterface
{
return $this->deadline;
}
public function setDeadline(\DateTimeInterface $deadline): self
{
$this->deadline = $deadline;
return $this;
}
public function getBudgetmission(): ?int
{
return $this->budgetmission;
}
public function setBudgetmission(int $budgetmission): self
{
$this->budgetmission = $budgetmission;
return $this;
}
/*public function getCodeapemission(): ?string
{
return $this->codeapemission;
}
public function setCodeapemission(string $codeapemission): self
{
$this->codeapemission = $codeapemission;
return $this;
}*/
public function getPrioritymission(): ?string
{
return $this->prioritymission;
}
public function setPrioritymission(string $prioritymission): self
{
$this->prioritymission = $prioritymission;
return $this;
}
/**
* #return Collection<int, tag>
*/
public function getIdtagmissionassign(): Collection
{
return $this->tags;
}
public function addIdtagmissionassign(tag $tags): self
{
if (!$this->tags->contains($tags)) {
$this->tags->add($tags);
}
return $this;
}
public function removeIdtagmissionassign(tag $tags): self
{
$this->tags->removeElement($tags);
return $this;
}
public function getIduser(): ?user
{
return $this->iduser;
}
public function setIduser(?user $iduser): self
{
$this->iduser = $iduser;
return $this;
}
/**
* #return Collection<int, user>
*/
public function getIdmissionassign(): Collection
{
return $this->idmissionassign;
}
public function addIdmissionassign(user $idmissionassign): self
{
if (!$this->idmissionassign->contains($idmissionassign)) {
$this->idmissionassign->add($idmissionassign);
}
return $this;
}
public function removeIdmissionassign(user $idmissionassign): self
{
$this->idmissionassign->removeElement($idmissionassign);
return $this;
}
public function getRemote(): ?string
{
return $this->remote;
}
public function setRemote(string $remote): self
{
$this->remote = $remote;
return $this;
}
public function __toString(){
return $this->titlemission;
}
public function getDatecreation(): ?\DateTimeInterface
{
return $this->datecreation;
}
public function setDatecreation(\DateTimeInterface $datecreation): self
{
$this->datecreation = $datecreation;
return $this;
}
}
This is my DQL request but i use Querybuilder without understand the concept. Could you help me ?
public function selectMissionWhereTag($tag){
return $this->createQueryBuilder('mi')
->select('mi.titlemission','mi.descriptionmission','mi.onsetdate','mi.deadline','mi.budgetmission','mi.prioritymission')
->leftJoin(User::class,'user', 'WITH', 'mi.iduser_id = user.id')
->leftJoin(Tag::class,'tag', 'WITH' , 'mi.tags = tag.id')
->where('tag.nomtag = :nomtag')
->setParameter('nomtag',$tag)
->getQuery()
->getResult();
}
Many thanks :)
To better understand how the QueryBuilder works I would recommend reading through how Doctrine ORM Mapping works first: Basic Mapping and Association Mapping
QueryBuilder's Joins use this mapping in a way that looks pretty similar to how you would do things with getters in your PHP code.
For example in a controller to get the tags of a mission you would do something like $tags = $mission->getTags(); well in QueryBuilder you simply do ->leftJoin('mi.tags', 'tag') and that's it. You don't have to worry about WITH unless you need extra conditions.
As you might have guessed there is a lot more to it but this should be enough to get you going.
I'm developping a quotation tool with NextJS and an API developed with Symfony and API Platform.
A Quotation has a collection of QuotationRow which is extended by three others entities : QuotationRowProduct, QuotationRowText, QuotationRowSubtotal.
Below, the entire entities:
Quotation.php
<?php
namespace App\Entity\Quotation;
use DateTime;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Quotation\QuotationRow;
use App\Repository\QuotationRepository;
use ApiPlatform\Core\Annotation\ApiFilter;
use Doctrine\Common\Collections\Collection;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\DateFilter;
use App\Entity\Client;
use App\Entity\ClientAgency;
#[ORM\Entity(repositoryClass: QuotationRepository::class)]
#[ApiResource(
normalizationContext: [
"groups" => ["Quotation:get"]
],
denormalizationContext: [
"groups" => ["Quotation:post"]
]
)]
#[ApiFilter(DateFilter::class, properties: ['createdAt' => DateFilter::EXCLUDE_NULL])]
#[HasLifecycleCallbacks]
class Quotation
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column()]
#[Groups(["Quotation:get"])]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?string $status = null;
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?int $validityPeriod = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?string $comment = null;
#[ORM\OneToMany(mappedBy: 'quotation', targetEntity: QuotationRow::class, orphanRemoval: true, cascade: ["all"])]
#[Groups(["Quotation:get", "Quotation:post"])]
#[ORM\OrderBy(["position" => "ASC"])]
private $quotationRows;
#[ORM\ManyToOne]
#[Groups(["Quotation:get", "Quotation:post"])]
#[Assert\NotBlank(message: "Le client est requis")]
private ?Client $client = null;
#[ORM\ManyToOne]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?ClientAgency $clientAgency = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
#[Groups(["Quotation:get"])]
private ?\DateTimeInterface $createdAt = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
#[Groups(["Quotation:get"])]
private ?\DateTimeInterface $updatedAt = null;
public function __construct()
{
$this->quotationRows = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
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 getStatus(): ?string
{
return $this->status;
}
public function setStatus(string $status): self
{
$this->status = $status;
return $this;
}
public function getValidityPeriod(): ?int
{
return $this->validityPeriod;
}
public function setValidityPeriod(int $validityPeriod): self
{
$this->validityPeriod = $validityPeriod;
return $this;
}
public function getComment(): ?string
{
return $this->comment;
}
public function setComment(?string $comment): self
{
$this->comment = $comment;
return $this;
}
public function getClient(): ?Client
{
return $this->client;
}
public function setClient(?Client $client): self
{
$this->client = $client;
return $this;
}
/**
* #return Collection<int, QuotationRow>
*/
public function getQuotationRows(): Collection
{
return $this->quotationRows;
}
public function addQuotationRow(QuotationRow $quotationRow): self
{
if (!$this->quotationRows->contains($quotationRow)) {
$this->quotationRows[] = $quotationRow;
$quotationRow->setQuotation($this);
}
return $this;
}
public function removeQuotationRow(QuotationRow $quotationRow): self
{
if ($this->quotationRows->removeElement($quotationRow)) {
// set the owning side to null (unless already changed)
if ($quotationRow->getQuotation() === $this) {
$quotationRow->setQuotation(null);
}
}
return $this;
}
public function getClientAgency(): ?ClientAgency
{
return $this->clientAgency;
}
public function setClientAgency(?ClientAgency $clientAgency): self
{
$this->clientAgency = $clientAgency;
return $this;
}
#[ORM\PrePersist]
public function prePersist()
{
$currDateTime = new DateTime();
$this->createdAt = $currDateTime;
$this->updatedAt = $currDateTime;
$this->status = "waiting";
}
#[ORM\PreUpdate]
public function preUpdate()
{
$this->updatedAt = new DateTime();
}
}
QuotationRow.php
<?php
namespace App\Entity\Quotation;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Quotation\Quotation;
use App\Entity\Quotation\QuotationRowText;
use App\Repository\QuotationRowRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Entity\Quotation\QuotationRowProduct;
use App\Entity\Quotation\QuotationRowSubtotal;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: QuotationRowRepository::class)]
#[ApiResource]
#[ORM\InheritanceType("SINGLE_TABLE")]
#[ORM\DiscriminatorColumn(name: "type", type: "string")]
#[ORM\DiscriminatorMap([
"product" => QuotationRowProduct::class,
"subtotal" => QuotationRowSubtotal::class,
"text" => QuotationRowText::class,
])]
class QuotationRow
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(["Quotation:get"])]
protected int $id;
#[ORM\ManyToOne(targetEntity: Quotation::class, inversedBy: 'quotationRows')]
#[ORM\JoinColumn(nullable: false)]
protected ?Quotation $quotation;
#[Groups(["Quotation:get", "Quotation:post"])]
#[ORM\Column]
protected ?int $position;
public function getId(): ?int
{
return $this->id;
}
public function getQuotation(): ?Quotation
{
return $this->quotation;
}
public function setQuotation(?Quotation $quotation): self
{
$this->quotation = $quotation;
return $this;
}
public function getPosition(): int
{
return $this->position;
}
public function setPosition(int $position)
{
$this->position = $position;
}
public function setType($type = "unknown"): self
{
$this->type = "unknown";
return $this;
}
}
QuotationRowProduct.php
<?php
namespace App\Entity\Quotation;
use App\Entity\VAT;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\QuotationRowRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: QuotationRowRepository::class)]
#[ApiResource]
class QuotationRowProduct extends QuotationRow
{
const TYPE = "product";
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private float $preTaxPrice;
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private int $quantity;
#[ORM\Column(type: Types::TEXT)]
#[Groups(["Quotation:get", "Quotation:post"])]
private string $description;
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private float $preTaxDiscount;
#[ORM\Column]
#[Groups(["Quotation:get", "Quotation:post"])]
private float $discountRate;
#[ORM\ManyToOne(targetEntity: VAT::class)]
#[ORM\JoinColumn(nullable: true)]
#[Groups(["Quotation:get", "Quotation:post"])]
private ?VAT $vat;
#[Groups(["Quotation:get", "Quotation:post"])]
public function getType(): string
{
return self::TYPE;
}
public function getPreTaxPrice(): ?float
{
return $this->preTaxPrice;
}
public function setPreTaxPrice(float $preTaxPrice): self
{
$this->preTaxPrice = $preTaxPrice;
return $this;
}
public function getQuantity(): ?int
{
return $this->quantity;
}
public function setQuantity(int $quantity): self
{
$this->quantity = $quantity;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
public function getPreTaxDiscount(): ?float
{
return $this->preTaxDiscount;
}
public function setPreTaxDiscount(float $preTaxDiscount): self
{
$this->preTaxDiscount = $preTaxDiscount;
return $this;
}
public function getDiscountRate(): ?float
{
return $this->discountRate;
}
public function setDiscountRate(float $discountRate): self
{
$this->discountRate = $discountRate;
return $this;
}
public function getVat(): ?VAT
{
return $this->vat;
}
public function setVat(?VAT $vat): self
{
$this->vat = $vat;
return $this;
}
public function setType($type = "product"): self
{
$this->type = "product";
return $this;
}
// Utility
public function getSubtotalWithoutVAT()
{
$price = $this->preTaxPrice * $this->quantity;
return $price - ($price * $this->discountRate / 100);
}
public function getSubtotalWithVAT()
{
$price = $this->getSubtotalWithoutVAT();
return $price + ($price * $this->vat->getRate() / 100);
}
}
QuotationRowSubtotal.php
<?php
namespace App\Entity\Quotation;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Quotation\QuotationRow;
use App\Repository\QuotationRowRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: QuotationRowRepository::class)]
#[ApiResource]
class QuotationRowSubtotal extends QuotationRow
{
const TYPE = "subtotal";
#[Groups(["Quotation:get", "Quotation:post"])]
public function getType(): string
{
return self::TYPE;
}
private function getAllRows()
{
return $this->quotation->getQuotationRows();
}
/**
* Return all subtotals in the Quotation#rows list
* #return QuotationRowSubtotal[]
*/
private function getSubtotalRows()
{
$filtered = [];
foreach ($this->getAllRows() as $row) {
if ($row instanceof QuotationRowSubtotal) {
$filtered[] = $row;
}
}
return $filtered;
}
/**
* Get the previous Subtotal priority
* #return integer
*/
private function getPrevSubtotalPos()
{
// Reverse result for find more quicker the last subtotal
$subtotalRows = array_reverse($this->getSubtotalRows());
foreach ($subtotalRows as $row) {
if ($this->position > $row->getPosition()) {
return $row->getPosition();
}
}
// Return -1 if any result for the next function condition
return -1;
}
/**
* Return the Subtotal from the last Subtotal without VAT
* #return float
*/
#[Groups(["Quotation:get"])]
public function getSubtotal()
{
$lastPosition = $this->getPrevSubtotalPos();
$total = 0;
foreach ($this->getAllRows() as $row) {
if ($row->getPosition() < $this->position && $row->getPosition() > $lastPosition) {
if ($row instanceof QuotationRowProduct) {
$total += $row->getSubtotalWithoutVAT();
}
}
}
return $total;
}
/**
* Return the Subtotal from the last Subtotal with VAT
* #return float
*/
#[Groups(["Quotation:get"])]
public function getSubtotalWithVat()
{
$lastPosition = $this->getPrevSubtotalPos();
$total = 0;
foreach ($this->getAllRows() as $row) {
if ($row->getPosition() < $this->position && $row->getPosition() > $lastPosition) {
if ($row instanceof QuotationRowProduct) {
$total += $row->getSubtotalWithVAT();
}
}
}
return $total;
}
}
QuotationRowText.php
<?php
namespace App\Entity\Quotation;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Quotation\QuotationRow;
use App\Repository\QuotationRowRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping\HasLifecycleCallbacks;
use Symfony\Component\Serializer\Annotation\Groups;
#[ORM\Entity(repositoryClass: QuotationRowRepository::class)]
#[ApiResource]
class QuotationRowText extends QuotationRow
{
const TYPE = "text";
#[ORM\Column(type: Types::TEXT)]
#[Groups(["Quotation:get", "Quotation:post"])]
private string $text;
#[Groups(["Quotation:get", "Quotation:post"])]
public function getType(): string
{
return self::TYPE;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(string $text): self
{
$this->text = $text;
return $this;
}
}
As you can see above in Quotation.php, the collection of QuotationRow is persisted by the Quotation entity.
The probleme is when I do a POST request to the API on /api/quotations endpoint, the API throw an SQL error : "column 'type' cannot be null", but 'type' is the discriminator column.
So I tried somethings before write this topic:
Send 'type' in the body request to the API: it's ignored by the API
Map $type as a protected property of QuotationRow : Makes an error because column 'type' already exsists in table 'quotation_row'.
I would like persist all QuotationRow from the Quotation API endpoint . Is there a way to persist a polymorphic collection in OneToMany context from the owner side ? Maybe by wrting a custom API Controller ?
Note that: there is no problem to update QuotationRow when they already exist in database, only to persist them.
I'm using Symfony + api-platform in their latest versions as of today.
I've got a User Entity and a Team entity which are related through a ManyToMany ORM relation.
A User can have several Teams a Team can have several Users, the Team "owns" the relation.
Once my user is logged in through a JWT Token, I would like the endpoint GET /teams to only send back the Teams in which the identified User is part of.
Here is my User entity :
<?php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\GetCollection;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ApiResource(operations: [
new Get(),
new GetCollection()
])]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
/**
* #var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
/** #var book[] Available reviews for this book. */
#[ORM\OneToMany(targetEntity: Book::class, mappedBy: 'user', cascade: ['persist', 'remove'])]
public iterable $books;
#[ORM\ManyToMany(targetEntity: Team::class, inversedBy: 'users')]
private Collection $teams;
public function __construct()
{
$this->teams = new ArrayCollection();
}
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 getUserIdentifier(): 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 PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
/**
* Méthode getUsername qui permet de retourner le champ qui est utilisé pour l'authentification.
*
* #return string
*/
public function getUsername(): string {
return $this->getUserIdentifier();
}
/**
* #return Collection<int, Team>
*/
public function getTeams(): Collection
{
return $this->teams;
}
public function addTeam(Team $team): self
{
if (!$this->teams->contains($team)) {
$this->teams->add($team);
}
return $this;
}
public function removeTeam(Team $team): self
{
$this->teams->removeElement($team);
return $this;
}
}
Here is my Team Entity :
<?php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use App\Repository\TeamRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\User;
#[ORM\Entity(repositoryClass: TeamRepository::class)]
#[ApiResource(security: "is_granted('ROLE_USER')")]
class Team
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 100)]
private ?string $name = null;
#[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'teams')]
private Collection $users;
public function __construct()
{
$this->users = 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, User>
*/
public function getUsers(): Collection
{
return $this->users;
}
public function addUser(User $user): self
{
if (!$this->users->contains($user)) {
$this->users->add($user);
$user->addTeam($this);
}
return $this;
}
public function removeUser(User $user): self
{
if ($this->users->removeElement($user)) {
$user->removeTeam($this);
}
return $this;
}
}
Here is my CurrentUserExtension Class that filters result based on the current user :
<?php
// api/src/Doctrine/CurrentUserExtension.php
namespace App\Doctrine;
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use App\Entity\Book;
use App\Entity\Team;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Security\Core\Security;
final class CurrentUserExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
{
$this->addWhere($queryBuilder, $resourceClass);
}
public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void
{
$this->addWhere($queryBuilder, $resourceClass);
}
private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
{
if ((Book::class !== $resourceClass && Team::class !== $resourceClass) || $this->security->isGranted('ROLE_ADMIN') || null === $user = $this->security->getUser()) {
return;
}
$rootAlias = $queryBuilder->getRootAliases()[0];
if (Team::class == $resourceClass){
$queryBuilder->andWhere(sprintf('%s.users = :current_user', $rootAlias));
//trigger_error($queryBuilder);
}
$queryBuilder->setParameter('current_user', $user->getId());
}
}
Obviously it doesn't work because of the nature of the relation existing between the two tables. I also tried to use the query builder to use leftjoin and join the user_team table. But since the user_team table is not an Entity it failed.
Here is an SQL equivalent of what I would like to get as a result :
select * from team t
left join user_team ut on ut.team_id = t.id
where user_id = :current_user
You should be able to just check if your user is IN or MEMBER OF one of your team $users.
Your query was close, try updating it with:
private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
{
if ((Book::class !== $resourceClass && Team::class !== $resourceClass) || $this->security->isGranted('ROLE_ADMIN') || null === $user = $this->security->getUser()) {
return;
}
$rootAlias = $queryBuilder->getRootAliases()[0];
if (Team::class == $resourceClass){
$queryBuilder->andWhere(sprintf(':current_user MEMBER OF %s.users', $rootAlias));
$queryBuilder->setParameter('current_user', $user);
}
}
You could either use MEMBER OF or IN but in a manyToMany it's usually easier to use MEMBER OF.
Infos : I'm new to Symfony and Sonata
My objective : Encrypte the password only for the database side. I would be able to display this password in clear. All this for the field password in the entity Service.
What did i'm trying ? : I tryed to create a Doctrine Listener which use bcrypt encryption
security:
encoders:
App\Entity\Service: bcrypt
here is the HashPasswordLisetener.php in my App\Doctrine (in the $formMapper of my configureFormField function in App\Admin\ServiceAdmin.php i have a row like this ->add('password'))
<?php
namespace App\Doctrine;
use App\Entity\Service;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class HashPasswordListener implements EventSubscriber
{
private $passwordEncoder;
public function __construct(UserPasswordEncoderInterface $passwordEncoder)
{
$this->passwordEncoder = $passwordEncoder;
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof Service) {
return;
}
$this->encodePassword($entity);
}
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof Service) {
return;
}
$this->encodePassword($entity);
$em = $args->getEntityManager();
$meta = $em->getClassMetadata(get_class($entity));
$em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $entity);
}
public function getSubscribedEvents()
{
return ['prePersist', 'preUpdate'];
}
/**
* #param Service $entity
*/
private function encodePassword(Service $entity)
{
if (!$entity->getPlainPassword()) {
return;
}
$encoded = $this->passwordEncoder->encodePassword(
$entity,
$entity->getPlainPassword()
);
$entity->setPassword($encoded);
}
}
here is the Service entity in the App\Entity
<?php
namespace App\Entity;
use App\Admin\AbstractAdmin;
use App\Repository\ServiceRepository;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass=ServiceRepository::class)
*/
class Service implements UserInterface
{
use TimestampableEntity;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\Column(type="string", length=255)
*/
private $password;
/**
* #ORM\Column(type="text", nullable=true, length=255)
*/
private $comment;
/**
* #ORM\Column(type="string", length=255)
*/
private $identifier;
private $plainPassword;
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 getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getComment(): ?string
{
return $this->comment;
}
public function setComment(?string $comment): self
{
$this->comment = $comment;
return $this;
}
public function getIdentifier(): ?string
{
return $this->identifier;
}
public function setIdentifier(string $identifier): self
{
$this->identifier = $identifier;
return $this;
}
/**
* #return mixed
*/
public function getPlainPassword()
{
return $this->plainPassword;
}
/**
* #param mixed $plainPassword
*/
public function setPlainPassword($plainPassword): void
{
$this->plainPassword = $plainPassword;
$this->password = null;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function getSalt()
{
}
public function eraseCredentials()
{
$this->plainPassword = null;
}
public function getUsername()
{
return $this->identifier;
}
}
Also my sonata_admin.yaml :
app.doctrine.hash_password_listener:
class: App\Doctrine\HashPasswordListener
autowire : true
tags:
- { name: doctrine.event_subscriber, connection: 'default' }
The result it give me :
My Probleme :
I understand the function encodePassword is waiting for a UserInterface in 1st arg (instead of my entity) and the password to encrypt in 2nd arg but i don't understand who use this UserInterface ? Where i'm suppose to call it ? to get it ? to send it ?
I think i give lot of details but if i forgot something feel free to notice me ^^
Thanks the time you spend at least reading.
I had a probleme but i was coding an Hashing type of code but i was looking for an encrypting methode. but here is how i deal for having a working hashing :
Step 1 : implementing UserInterface in my Service entity
Step 2 : Replace UserPasswordEncoder for UserPasswordEncoderInterface in the Listener
Step 2.5 : Added all function needed for the UserInterface like eraseCredidential getSalt().. see : this for more details
Step 3 : Adding a getUsername which return this->identifier
Step 4 : using plainPasswords in form field instead of password
Step 5 : added a provider :
app_user:
entity:
class: 'App\Entity\Service'
property: 'identifier'
Step 6 : Using TextType::class for plainPassword form field type + fixing the use with :
Symfony\Component\Form\Extension\Core\Type\TextType
Step 7 : Working
(Special thanks to #msg who helped me a lot)