Symfony workflow initializes back itself when passing to another route - symfony

I'm using Symfony workflow on my project. I can pass the first transition and see that the next transitions are enabled with $workflow->getEnabledTransitions($entity);. When passing to another route which introduce the next transition (part of the enabled transitions) it seems that the workflow has initialized back itself when checking the enabled transitions.
Here is my workflow.yaml configuration file :
framework:
workflows:
produit_selection:
type: 'workflow' # or 'state_machine'
audit_trail:
enabled: true
marking_store:
type: 'single_state'
arguments:
- 'currentPlace'
supports:
- App\Entity\Produit
initial_place: initiale
places:
- initiale
- commande
- selectionne
- invalide_selection
transitions:
commander:
from: initiale
to: commande
selectionner:
from: commande
to: selectionne
invalider_selection:
from: commande
to: invalide_selection
Here is my commandeProduit controller action :
/**
* #Route("/produit/commande/{id<\d+>}", name="produit_commande")
* #param Request $request
* #param $id
* #param ProduitRequestCommandeUpdateHandler $updateHandler
* #return \Symfony\Component\HttpFoundation\Response
* #Security("has_role('ROLE_MARKETING')")
*/
public function commandeProduit(
Request $request,
$id,
ProduitRequestCommandeUpdateHandler $updateHandler,
Registry $workflows
) {
$repository = $this->getDoctrine()->getRepository(Produit::class);
/** #var Produit $produit */
$produit = $repository->find($id);
$produitRequest = ProduitRequest::createFromProduit($produit);
$form = $this->createForm(ProduitCommandeType::class, $produitRequest)->handleRequest($request);
$box = $produit->getBox();
if ($form->isSubmitted() AND $form->isValid()) {
$produit = $updateHandler->handle($produitRequest, $produit, $box);
if (null === $produit) {
$this->addFlash(
'error',
"La commande a été annulée car elle dépasse le budget."
);
} elseif ($produit->getCommande()) {
$workflow = $workflows->get($produit, 'produit_selection');
//$workflow->getMarking($produit);
if ($workflow->can($produit, 'commander')) {
try {
$workflow->apply($produit, 'commander');
} catch (TransitionException $exception) {
// ... if the transition is not allowed
}
}
$transitions = $workflow->getEnabledTransitions($produit);
/** #var Transition $transition */
foreach($transitions as $transition) {
$this->addFlash(
'error',
$transition->getName()
);
}
$this->addFlash(
'notice',
"La commande du produit a bien été prise en compte avec la quantité " . $produit->getQuantite() . "."
);
} else {
$this->addFlash(
'warning',
"Le produit n'est pas disponible."
);
}
return $this->redirectToRoute("produits_commande");
}
$fournisseur = $produit->getFournisseur();
return $this->render('produits/commande_produit.html.twig', [
'box' => $box,
'fournisseur' => $fournisseur,
'form' => $form->createView()
]);
}
And here is my selectionProduit controller action :
/**
* #Route("/produit/selection/{id<\d+>}", name="produit_selection")
* #param Request $request
* #param $id
* #param ProduitRequestUpdateHandler $updateHandler
* #param Registry $workflows
* #return \Symfony\Component\HttpFoundation\Response
* #Security("has_role('ROLE_MARKETING')")
*/
public function selectionProduit(
Request $request,
$id,
ProduitRequestUpdateHandler $updateHandler,
Registry $workflows
) {
$repository = $this->getDoctrine()->getRepository(Produit::class);
/** #var Produit $produit */
$produit = $repository->find($id);
$produitRequest = ProduitRequest::createFromProduit($produit);
$form = $this->createForm(ProduitSelectionType::class, $produitRequest)->handleRequest($request);
if($form->isSubmitted() AND $form->isValid()) {
$produit = $updateHandler->handle($produitRequest, $produit);
if ($produit->getSelectionne()) {
$workflow = $workflows->get($produit, 'produit_selection');
//$workflow->getMarking($produit);
$workflow->can($produit, 'selectionner');
try {
$workflow->apply($produit, 'selectionner');
} catch (TransitionException $exception) {
// ... if the transition is not allowed
}
$transitions = $workflow->getEnabledTransitions($produit);
/** #var Transition $transition */
foreach($transitions as $transition) {
$this->addFlash(
'error',
$transition->getName()
);
}
$this->addFlash(
'notice',
"Le produit a bien été sélectionné avec la quantité " . $produit->getQuantite() . ".");
} else {
$workflow = $workflows->get($produit, 'produit_selection');
//$workflow->getMarking($produit);
if ($workflow->can($produit, 'invalider_selection')) {
try {
$workflow->apply($produit, 'invalider_selection');
} catch (TransitionException $exception) {
// ... if the transition is not allowed
}
}
$transitions = $workflow->getEnabledTransitions($produit);
$this->addFlash(
'warning',
"Le produit n'a pas été sélectionné.");
}
return $this->redirectToRoute("produits_selection");
}
$box = $produit->getBox();
$fournisseur = $produit->getFournisseur();
return $this->render('produits/selection_produit.html.twig', [
'box' => $box,
'fournisseur' => $fournisseur,
'form' => $form->createView()
]);
}
Why the workflow doesn't transit to the next place after changing route ? How to fix that ? Thanks for help.

I found it. We have to flush the entity to store the current place :
$workflow->apply($produit, 'selectionner');
$this->getDoctrine()->getManager()->flush();
And in the entity we have a field defined like this :
/**
* #ORM\Column(type="string", length=100)
*/
public $currentPlace;

Related

App\Entity\Site object not found by the #ParamConverter annotation

I'm trying to create a new tempate in my project in symfony to put a climbing converter. But it doesn't work. I have the error:
App\Entity\Site object not found by the #ParamConverter annotation
I put the function in the entity "site" as following :
/**
* #Route("/site")
*/
class SiteController extends AbstractController
{
public function upload( KernelInterface $kernel): Response
{
$imagesDir = $kernel->getProjectDir().'/public/uploads'; // équivalent à $this->getParameter('images_directory')
dump($imagesDir) ;
return $this->render('site/show.html.twig');
}
/**
* #Route("/", name="site_index", methods={"GET"})
*/
public function index(SiteRepository $siteRepository): Response
{
return $this->render('site/index.html.twig', [
'sites' => $siteRepository->findAll(),
]);
}
/**
* #Route("/new", name="site_new", methods={"GET","POST"})
*/
public function new(Request $request): Response
{
$site = new Site();
$form = $this->createForm(SiteType::class, $site);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//on recupère les medias transmises
$media = $form->get('site')->getData();
//on boucle sur les medias
foreach($media as $medi){
//on génère un nouveau nom de fichier
$fichier = md5(uniqid()) . '.' . $medi->guessExtension();
//on copie le fichier dans le dossier img
$medi->move(
$this->getParameter('images_directory'),
$fichier
);
//on stocke l'image dans la bdd
$img = new Media();
$img->setNom($fichier);
$site->addMedium($img);
}
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($site);
$entityManager->flush();
return $this->redirectToRoute('site_index');
}
return $this->render('site/new.html.twig', [
'site' => $site,
'form' => $form->createView(),
]);
}
/**
* #Route("/{id}", name="site_show", methods={"GET"})
*/
public function show(Site $site, MediaRepository $mediarepository, $id): Response
{
$media = $mediarepository->findBy(
['site'=>$id]
);
return $this->render('site/show.html.twig', [
'site' => $site,
'media' => $media,
]);
}
/**
* #Route("/{id}/edit", name="site_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Site $site): Response
{
$form = $this->createForm(SiteType::class, $site);//j'appelle le form
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//on recupère les medias transmises
$media = $form->get('site')->getData();
//on boucle sur les medias
foreach($media as $medi){
//on génère un nouveau nom de fichier
$fichier = md5(uniqid()) . '.' . $medi->guessExtension();
//on copie le fichier dans le dossier img
$medi->move(
$this->getParameter('images_directory'),
$fichier
);
//on stocke l'image dans la bdd
$img = new Media();
$img->setNom($fichier);
$site->addMedium($img);
}
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('site_index');
}
return $this->render('site/edit.html.twig', [
'site' => $site,
'form' => $form->createView(),
]);
}
/**
* #Route("/{id}", name="site_delete", methods={"POST"})
*/
public function delete(Request $request, Site $site): Response
{
if ($this->isCsrfTokenValid('delete'.$site->getId(), $request->request->get('_token'))) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($site);
$entityManager->flush();
}
return $this->redirectToRoute('site_index');
}
/**
* #Route("/conv", name="site_converter")
*/
public function converter():Response
{
return $this->render('site/converter.html.twig');
}
}
I checked others answers about this problem, but I still can't find out the solution. Do you have any ideas?
I put some more code so that it would be easier to understand. I hope this would be usefull. It's the route for converter that makes me problem. Thanks
This happens because you have a route called /conv, you should declare it before declaring /{id} route, instead Symfony searches for a Site object with id: conv which is not found.
Move converter route and method declaration before your show route and method declaration.

Method not found in entity Paginator from doctrine bundle

I'm trying to clean up some code done in a messy way and right now I'm having trouble with doctrine's paginator.
When I'm accessing a page that handle paginator in order to show all different articles of my blog I'm getting this error:
Neither the property "id" nor one of the methods "id()", "getid()"/"isid()"/"hasid()" or "__call()" exist and have public access in class "Doctrine\ORM\Tools\Pagination\Paginator".
In doctrine vendor bundle those methods are not set but my entity have them and I know that it is forbidden to edit a vendor file. I'm missing something because I don't know if I should extend my paginator entity and add those missing methods or is there a little bit more to do ?
I just started symfony and I know that my bases are not enough to understand it all by myself.
Thank you very much for you time and attention.
Here is my Article controller for route category:
/**
* #Route("/categorie/{id}", name="categorie")
*
* #param Request $request
* #param Helper $helper
* #param AuthorizationCheckerInterface $authChecker
* #param DocumentCategory $categorie
* #param TwitterService $twitterService
*
* #return RedirectResponse|Response
*/
public function categorie(
Request $request,
Helper $helper,
AuthorizationCheckerInterface $authChecker,
DocumentCategory $categorie,
TwitterService $twitterService
) {
if (!$authChecker->isGranted('IS_AUTHENTICATED_FULLY')
&& !$authChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return $this->redirectToRoute('login');
}
$page = (int) ($request->get('page'));
if (0 === $page) {
$page = 1;
}
$userType = $this->getDoctrine()->getRepository('App:User')
->getManagerExpertCollabo($this->getUser());
$articleAlaUneListe = [];
$articleIdListe = $helper->getArticleIdAuth($authChecker);
$articleListe = $this->getDoctrine()
->getRepository('App:Document')
->getPage(
self::ITEM_PER_PAGE * ($page - 1),
self::ITEM_PER_PAGE,
'document.dateCreated',
'DESC',
'(documentCategory.id = \''.$categorie->getId().'\' and '.$helper->baseRequestArticle().')',
7,
[],
$articleIdListe
);
[$articlePopulaireListe, $categorieListe, $totalPage] = $this->getPopularArticleList(
$articleListe,
$helper,
$articleIdListe
);
$articlesList->getDocuments();
$feedData = $twitterService->getTwitterFeed();
return $this->render('article/list.html.twig', [
'pageClass' => 'backoffice withFooterLarge dashboard',
'totalPage' => $totalPage,
'page' => $page,
'feedData' => $feedData,
'categorieListe' => $categorieListe,
'categorie' => $categorie,
'articleAlaUneListe' => $articleAlaUneListe,
'articlePopulaireListe' => $articlePopulaireListe,
'articleListe' => $articleListe, ]);
}
Here is the document entity for categories field:
/**
* #ORM\JoinTable(name="ht_lk_document_category"),
* #ORM\ManyToMany(targetEntity="App\Entity\DocumentCategory", inversedBy="documents")
*/
private $categories;
/**
* #return Collection|array<DocumentCategory>
*/
public function getCategories(): Collection
{
return $this->categories;
}
public function setCategories($category): self
{
$this->categories = $category;
return $this;
}
Here is the DocumentCategory entity :
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Document", mappedBy="categories")
*/
private $documents;
/**
* #return Collection|Document[]
*/
public function getDocuments(): Collection
{
return $this->documents;
}
Here is the Document Repository :
public function getPage($first_result, $max_results, $orderby, $direction, $criteria, $documentType = null, $searchWordArray = [], $articleIdListe = '')
{
$qb = $this->createQueryBuilder('docArticle');
$qb->select('docArticle')
->addSelect('documentCategory', 'documentCategory')
->addSelect('user', 'user')
/*
if(sizeof($searchWordArray) > 0){
$fieldIndice = 1;
foreach($searchWordArray as $searchWord){
$qb->andWhere('(document.name_fr LIKE ?'.$fieldIndice.' or document.name_en LIKE ?'.$fieldIndice.' or document.content_fr LIKE ?'.$fieldIndice.' or document.content_en LIKE ?'.$fieldIndice.')');
$qb->setParameter($fieldIndice++, '%'.$searchWord.'%');
}
} */
->leftJoin('docArticle.categories', 'documentCategory')
->leftJoin('docArticle.author', 'user')
->setFirstResult($first_result)
->setMaxResults($max_results);
if (!empty($criteria)) {
$qb->where('('.$criteria.')');
}
if (!empty($orderby)) {
$qb->orderBy($orderby, $direction);
}
$pag = new Paginator($qb->getQuery());
$qb->setFirstResult(0);
$qb->setMaxResults(PHP_INT_MAX);
$sql = $qb->getQuery()->getSql();
if ('()' !== $articleIdListe) {
$qb->where('(docArticle.id IN '.$articleIdListe);
}
$compte = \count($qb->getQuery()->getScalarResult());
return ['page' => $pag, 'compte' => $compte];
}
And finally here is the Document Category Repository :
/**
* #param $first_result
* #param $max_results
* #param $orderby
* #param $direction
* #param $criteria
* #param int|null $documentType
* #param array $searchWordArray
* #param string $articleIdListe
*
* #return array
*/
public function getPage(
$first_result,
$max_results,
$orderby,
$direction,
$criteria,
$documentType = null,
$searchWordArray = [],
$articleIdListe = ''
) {
$qb = $this->createQueryBuilder('document');
$qb->select('document')
->addSelect('documentCategory', 'documentCategory')
->addSelect('user', 'user')
->addSelect('documentType', 'documentType');
if (\count($searchWordArray) > 0) {
$fieldIndice = 1;
foreach ($searchWordArray as $searchWord) {
$qb->andWhere(
'('
.'document.name_fr LIKE ?'.$fieldIndice
.' or document.name_en LIKE ?'.$fieldIndice
.' or document.content_fr LIKE ?'.$fieldIndice
.' or document.content_en LIKE ?'.$fieldIndice
.')'
);
$qb->setParameter($fieldIndice++, '%'.$searchWord.'%');
}
}
if ($documentType) {
if (\mb_strlen($articleIdListe) > 3) {
$qb->andWhere('(documentType.id = :documentType OR document.id IN '.$articleIdListe.')')
->setParameter('documentType', $documentType);
} else {
$qb->andWhere('(documentType.id = :documentType)')
->setParameter('documentType', $documentType);
}
}
$qb->leftJoin('document.categories', 'documentCategory')
->leftJoin('document.documentType', 'documentType')
->leftJoin('document.author', 'user')
->setFirstResult($first_result)
->setMaxResults($max_results)
->andWhere('document.documentType<>6');
if (!empty($criteria)) {
$qb->andWhere('('.$criteria.')');
}
if (!empty($orderby)) {
$qb->orderBy($orderby, $direction);
}
$sql = $qb->getQuery()->getSql();
$pag = new Paginator($qb->getQuery());
dump($pag);
$qb->setFirstResult(0);
$qb->setMaxResults(PHP_INT_MAX);
$sql = $qb->getQuery()->getSql();
$compte = \count($qb->getQuery()->getScalarResult());
return ['page' => $pag, 'compte' => $compte];
}
/**
* #param int|null $documentType
*
* #return array
*/
public function getArticleIdList($documentType = null)
{
$qb = $this->createQueryBuilder('document');
$qb->select('document.id');
if ($documentType) {
$qb->where('(document.documentType = :documentType)')
->setParameter('documentType', $documentType);
}
$compte = $qb->getQuery()->getScalarResult();
return $compte;
}
(I deleted all unnecessary method for this question)
Add the following code to the entity class from which you want to show data:
public function getId(): ?string
{
return $this->id;
}
Or
if you do not want the property "id" to be displayed in the view, comment out or delete the lines of code in your Twig template for the entity in question. For example, delete
<td>{{ Category.id }}</td>

Symfony 3 - Cannot correctly manage my user roles in PROD environment

I have a problem on symfony 3.
I'm trying to manage my roles from a page I created.
The role system I use is the same as the one used by FOSUserBundle.
/**
* #ORM\Column(type="array")
*/
protected $roles = [];
/**
* Constructor
*/
public function __construct()
{
$this->setIsActif(true);
$this->roles = array();
}
/**
* {#inheritdoc}
*/
public function addRole($role)
{
$role = strtoupper($role);
if ($role === ['ROLE_USER']) {
return $this;
}
if (!in_array($role, $this->roles, true)) {
$this->roles[] = $role;
}
return $this;
}
/**
* {#inheritdoc}
* #return array
*/
public function getRoles()
{
return array_unique(array_merge(['ROLE_USER'], $this->roles));
}
/**
* Vérifie si l'utilisateur possède le rôle passé en paramètre
* {#inheritdoc}
*
* #param string
* #return bool
*/
public function hasRole($role)
{
return in_array(strtoupper($role), $this->getRoles(), true);
}
/**
* Supprimer un rôle
* {#inheritdoc}
*
* #return UserCas
*/
public function removeRole($role)
{
if (false !== $key = array_search(strtoupper($role), $this->roles, true)) {
unset($this->roles[$key]);
$this->roles = array_values($this->roles);
}
return $this;
}
/**
* Set roles
* {#inheritdoc}
*
* #return UserCas
*/
public function setRoles(array $roles)
{
$this->roles = array();
foreach ($roles as $role) {
$this->addRole($role);
}
return $this;
}
/**
* Réinitialise les rôles de l'utilisateur
*/
public function resetRoles()
{
$this->roles = [];
}
When I was in a "PREPROD" environment, everything was working, my roles were changing well. But since I switched to "PROD", when my user does not have a role (so it is automatically ROLE_USER), well 9 times out of 10, I can not assign any other role. But if he has a role other than ROLE_USER, then I can assign him any role. Why does not it work in preprod? I do not know...
On twig, I have my list of users with a list option where I choose the new role. Then it updates the page. My request retrieves the label of the new role and is assigned
Twig:
<form action="{{path('user_role_change', {'username': unUser.username })}}" method="POST">
<select id="role" name="role" onChange="this.form.submit();">
{% for unRole in listeRoles %}
<option value="{{unRole}}" {% if unRole == 'User' %} selected {% endif %}>{{unRole}}</option>
{% endfor %}
</select>
</form>
Controller:
/**
* Change le rôle d'un utilisateur lorsque l'on change l'option dans l'option list des rôles d'un utilisateur
*
* #Route("/changeRole/{username}", name="user_role_change")
* #Method("GET")
*/
public function changeRoleAction(Request $request, $username)
{
$em = $this->getDoctrine()->getManager();
$idRole=$request->get('role');
$user = $em->getRepository('PagesBundle:UserCas')->findOneByUsername($username); // On cherche si c'est un UserCas (user académique)
if($user == null)
{
$user = $em->getRepository('PagesBundle:User')->findOneByUsername($username); // On cherche si c'est un User externe
$nouveauRole = $this->getNouveauRole($idRole);
$user->setRoles($nouveauRole);
$em->persist($user);
$em->flush();
return $this->redirectToRoute('roles_index'); //redirection vers la page de gestion des Informations
}
/**
* Méthode inverse de $this->switchRole()
* Elle renvoi le rôle en type array de sorte à ce qu'elle soit injectable dans l'attribut roles de l'utilisateur ( ex: "Admin" => "ROLE_ADMIN")
*
* #param int $nomRole
* #return array
*/
public function getNouveauRole($nomRole)
{
switch($nomRole)
{
case "Admin":
$role = ['ROLE_ADMIN'];
break;
case "Packages":
$role = ['ROLE_PACKAGES'];
break;
case "Infos":
$role = ['ROLE_INFOS'];
break;
default:
$role = [];
break;
}
return $role;
}
Can someone help me please ?
Try to fix your addRole() method like the following:
/**
* {#inheritdoc}
*/
public function addRole($role)
{
$role = strtoupper($role);
if ($role === 'ROLE_USER') {
return $this;
}
if (!in_array($role, $this->roles, true)) {
$this->roles[] = $role;
}
return $this;
}

Symfony Two edit actions (activate/desactivate) one is working and the other doesn't work

I have two actions that edit the entity 'user' attribute 'etat' : activateAction makes the 'etat' equals to 1 if it was equal to 0, else it returns a flashbag message 'the account is already activated', and the desactivateAction is supposed to do the opposite, but it doesn't work!!! Here is the code of both activate and desactivate actions:
/**
* #Route("/admin/gestEtat/act/{iduser}", name="act")
*
* #Template()
*/
public function activateAction($iduser)
{
$user=new user();
$em=$this->getDoctrine()->getManager();
$repository = $em->getRepository("CNAMCMSBundle:user");
$user = $repository->find($iduser);
if($user)
{
if ($user->getEtat()==1) {
$this->get("session")->getFlashBag()->add('act',"Ce compte est déjà activé!");
return $this->redirectToRoute('gestEtat',
array());
}
elseif ($user->getEtat()==0) {
$user->setEtat('1');
$em->merge($user);
$em->flush();
return $this->redirectToRoute('gestEtat',
array());
}
}
}
/**
* #Route("/admin/gestEtat/desact/{id}",name="desact")
*
* #Template()
*/
public function desactivateAction($id)
{
$user=new user();
$em=$this->getDoctrine()->getManager();
$repository = $em->getRepository("CNAMCMSBundle:user");
$user = $repository->find($id);
//$session = new Session();
//$session->start();
//$users=$session->get('users_table');
if($user)
{
if ($user->getEtat()==0) {
$this->get("session")->getFlashBag()->add('desact',"Ce compte est déjà désactivé!");
// return $this->render('CNAMCMSBundle:Default:gestEtat.html.twig',
return $this->redirectToRoute('gestEtat',
array());
}
elseif ($user->getEtat()==1) {
$user->setEtat('0');
$em->merge($user);
$em->flush();
// return $this->render('CNAMCMSBundle:Default:gestEtat.html.twig',
return $this->redirectToRoute('gestEtat',
array());
}
}
}
Seems like you're performing setEtat('0') by passing in the string '0'. If the entity variable is a boolean, you should send it as a (true/false) or (1/0). If it is a string, you should be checking in your code elseif (getEtat()=='1')
The way it stands, checking if (getEtat()==1) will be the same as if (getEtat()), which will return true if getEtat() is not explicitly a false/null boolean, or a null variable.

JMSSerializer deserialize entity by id

i'm using JMSSerializer to deserialize a JSON request and i'm having troubles with ManyToOne relations. I would like to deserialize the relation entity from a id given. Example:
Class Game {
/**
* #var Team
*
* #ORM\ManyToOne(targetEntity="Team")
* #ORM\JoinColumn(name="home_team_id", referencedColumnName="id")
* #JMSSerializer\SerializedName("home")
*/
private $homeTeam;
/**
* #ORM\ManyToOne(targetEntity="Team")
* #ORM\JoinColumn(name="visitor_team_id", referencedColumnName="id")
* #JMSSerializer\SerializedName("visitor")
*/
private $visitorTeam;
}
So when i get this Json
{"home": "id1", "visitor": "id2"}
Get the related entities. Any clouds?? i can't figure it out
Thanks in advance
Custom serializer handler allows to do it.
At first, you need to create your own serialization handler. Something like this:
<?php
namespace AppBundle\Serializer\Handler;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
use JMS\Serializer\Context;
use JMS\Serializer\Exception\InvalidArgumentException;
use JMS\Serializer\GenericDeserializationVisitor;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\VisitorInterface;
use JMS\Serializer\GraphNavigator;
class EntityHandler implements SubscribingHandlerInterface
{
/**
* #var RegistryInterface
*/
protected $registry;
/**
* #return array
*/
public static function getSubscribingMethods()
{
$methods = [];
foreach (['json', 'xml', 'yml'] as $format) {
$methods[] = [
'type' => 'Entity',
'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
'format' => $format,
'method' => 'deserializeEntity',
];
$methods[] = [
'type' => 'Entity',
'format' => $format,
'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
'method' => 'serializeEntity',
];
}
return $methods;
}
/**
* EntityHandler constructor.
* #param RegistryInterface $registry
*/
public function __construct(RegistryInterface $registry)
{
$this->registry = $registry;
}
/**
* #param VisitorInterface $visitor
* #param $entity
* #param array $type
* #param Context $context
* #return mixed
*/
public function serializeEntity(VisitorInterface $visitor, $entity, array $type, Context $context)
{
$entityClass = $this->getEntityClassFromParameters($type['params']);
if (!$entity instanceof $entityClass) {
throw new InvalidArgumentException(
sprintf("Entity class '%s' was expected, but '%s' got", $entityClass, get_class($entity))
);
}
$entityManager = $this->getEntityManager($entityClass);
$primaryKeyValues = $entityManager->getClassMetadata($entityClass)->getIdentifierValues($entity);
if (count($primaryKeyValues) > 1) {
throw new InvalidArgumentException(
sprintf("Composite primary keys does'nt supported now (found in class '%s')", $entityClass)
);
}
if (!count($primaryKeyValues)) {
throw new InvalidArgumentException(
sprintf("No primary keys found for entity '%s')", $entityClass)
);
}
$id = array_shift($primaryKeyValues);
if (is_int($id) || is_string($id)) {
return $visitor->visitString($id, $type, $context);
} else {
throw new InvalidArgumentException(
sprintf(
"Invalid primary key type for entity '%s' (only integer or string are supported",
$entityClass
)
);
}
}
/**
* #param GenericDeserializationVisitor $visitor
* #param string $id
* #param array $type
*/
public function deserializeEntity(GenericDeserializationVisitor $visitor, $id, array $type)
{
if (null === $id) {
return null;
}
if (!(is_array($type) && isset($type['params']) && is_array($type['params']) && isset($type['params']['0']))) {
return null;
}
$entityClass = $type['params'][0]['name'];
$entityManager = $this->getEntityManager($entityClass);
return $entityManager->getRepository($entityClass)->find($id);
}
/**
* #param array $parameters
* #return string
*/
protected function getEntityClassFromParameters(array $parameters)
{
if (!(isset($parameters[0]) && is_array($parameters[0]) && isset($parameters[0]['name']))) {
throw new InvalidArgumentException('Entity class is not defined');
}
if (!class_exists($parameters[0]['name'])) {
throw new InvalidArgumentException(sprintf("Entity class '%s' is not found", $parameters[0]['name']));
}
return $parameters[0]['name'];
}
/**
* #param string $entityClass
* #return EntityManagerInterface
*/
protected function getEntityManager($entityClass)
{
$entityManager = $this->registry->getEntityManagerForClass($entityClass);
if (!$entityManager) {
throw new InvalidArgumentException(
sprintf("Entity class '%s' is not mannaged by Doctrine", $entityClass)
);
}
return $entityManager;
}
}
Then you should register it in your service configuration file. If you use yaml, it will be something like that:
custom_serializer_handle:
class: AppBundle\Serializer\Handler\EntityHandler
arguments: ['#doctrine']
tags:
- {name: 'jms_serializer.subscribing_handler'}
In your entity, define JMSSerializer Type annotation
/**
* #var Team
* * #ORM\ManyToOne(targetEntity="Team")
* #ORM\JoinColumn(name="home_team_id", referencedColumnName="id")
* #JMSSerializer\SerializedName("home")
* #JMSSerializer\Type("Entity<AppBundle\Entity\Team>")
* List item
*/
private $homeTeam;
Don't forget clear caches.
That's all.

Resources