Symfony, doctrine Removing Associations setEntity(null) not allowed - symfony

I need to remove the association between two objects with the following relation
Devis Entity :
class Devis
{
/**
* #var \stdClass
*
* #ORM\ManyToOne(targetEntity="DevisBundle\Entity\Client", inversedBy="devis", cascade={"persist"})
* #ORM\JoinColumn(nullable=false, nullable=true)
*/
private $client;
/**
* Set client
*
* #param \DevisBundle\Entity\Client $client
* #return Devis
*/
public function setClient(\DevisBundle\Entity\Client $client)
{
$this->client = $client;
return $this;
}
Client entity:
class Client
{
/**
* #var \stdClass
*
* #ORM\OneToMany(targetEntity="DevisBundle\Entity\Devis", mappedBy="client", cascade={"remove"})
*/
private $devis;
I tried to folllow the doc from doctrine : http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#removing-associations
$devis = $em->getRepository('DevisBundle:Devis')->findOneById($devisId);
$client = $em->getRepository('DevisBundle:Client')->findOneById($devis->getClient());
$client->getDevis()->removeElement($devis);
$devis->setClient(null);
//$em->persist($devis);
$em->flush();
dump($devis);
But I have this error :
Catchable Fatal Error: Argument 1 passed to DevisBundle\Entity\Devis::setClient() must be an instance of DevisBundle\Entity\Client, null given, called in....

Change your set Method like this :
public function setClient(\DevisBundle\Entity\Client $client = null)
{
$this->client = $client;
return $this;
}
This is also how it would be generated with the command doctrine:generate:entities.

Related

How to order by entity property in symfony?

I'm trying to get the "demands" of a user.
User can have some demands and a demand have only one user (OneToMany)
This is my User entity (Utilisateur in french) :
class Utilisateur extends AbstractEntity implements UserInterface, PasswordAuthenticatedUserInterface
{
/**
* #ORM\Id
* #ORM\Column(type="ulid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UlidGenerator::class)
*/
private Ulid $id;
/**
* #ORM\OneToMany(targetEntity=DemandeTransport::class, mappedBy="utilisateur", orphanRemoval=true)
*/
private Collection $demandeTransports;
And my demands entity :
class DemandeTransport extends AbstractEntity
{
/**
* #ORM\Id
* #ORM\Column(type="ulid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UlidGenerator::class)
*/
private Ulid $id;
/**
* #ORM\ManyToOne(targetEntity=Utilisateur::class, inversedBy="demandeTransports")
* #ORM\JoinColumn(nullable=false)
*/
private Utilisateur $utilisateur;
My controller receiving the request :
/**
* #throws Exception
*/
#[Route('/liste_propositions_transporteur', name: 'liste_propositions_transporteur', methods: ['GET'])]
public function listePropositionsTransporteur(Request $request): Response
{
return match ($request->getMethod()) {
'GET' => new Response(json_encode(['success' => true, 'data' => $this->propositionsTransportService->handleListePropositionsByUser($this->getUser())])),
default => new Response(404),
};
}
The service handling the request and retreiving the demands :
/**
* #param UserInterface $user
* #return array
*/
public function handleListePropositionsByUser(UserInterface $user) : array
{
$propositions = [];
foreach ($this->propositionTransportRepository->findPropositionsByUtilisateur($user) as $propositionTransport) {
$propositions[] = DemandeTransportHelper::serializePropositionDemande($propositionTransport);
}
return $propositions;
}
And the DQL :
/**
* #param UserInterface $user
* #return mixed
*/
public function findPropositionsByUtilisateur(UserInterface $user) : mixed
{
$q = $this->createQueryBuilder('p')
->where('p.utilisateur = :utilisateur')
->setParameters([
'utilisateur' => $user
])
->orderBy('p.dateCreation', 'DESC');
return $q->getQuery()->getResult();
}
So :
When i'm doing $utilisateur->getDemandesTransports() : it works by showing me all the demands.
Well, but when I'm trying to get them by DQL (cause I want them orderd by), it returns me 0 results...
Solved by setting the parameter type :
->setParameter('utilisateur', $utilisateur->getId(), 'ulid')
I'm using ULID (UUID like) on IDs.
https://symfony.com/doc/current/components/uid.html#working-with-ulids
With annotations
You can order your data by specifying sorting in your $demandeTransports property annotations.
/**
* #ORM\OneToMany(targetEntity=DemandeTransport::class, mappedBy="utilisateur", orphanRemoval=true)
* #ORM\OrderBy({"dateCreation" = "DESC"})
*/
private Collection $demandeTransports;
So when you call $utilisateur->getDemandesTransports() you will get ordered data.
With DQL
Also if you still want to use DQL then you should change your query to this as you need to join the Utilisateur entity then you can use the desired properties
$q = $this->createQueryBuilder('p')
->join('p.utilisateur', 'u')
->where('u.utilisateur = :utilisateur')
->setParameters([
'utilisateur' => $user
])
->orderBy('u.dateCreation', 'DESC');

Symfony/PhpUnit - How to test that an Exception is throws or not in a Service

I'm beginner with tests and I want to test my ValidatorService which throws an InvalidDataException when an entity data are not valid.
My ValidatorServiceTest function :
public function testValidatorForUser()
{
$validatorMock = $this->createMock(ValidatorInterface::class);
$contraintViolationMock = $this->createMock(ConstraintViolationListInterface::class);
$validatorMock->expects($this->once())
->method('validate')
->with()
->willReturn($contraintViolationMock);
$validatorService = new ValidatorService($validatorMock);
$user = new User();
$user->setEmail('test');
$validatorService->validate($user);
$this->expectException(InvalidDataException::class);
}
My ValidatorService :
class ValidatorService
{
/**
* #var ValidatorInterface
*/
private ValidatorInterface $validator;
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
}
/**
* #param $value
* #param null $constraints
* #param null $groups
* #throws InvalidDataException
*/
public function validate($value, $constraints = null, $groups = null)
{
$errors = $this->validator->validate($value, $constraints, $groups);
if (count($errors) > 0) {
throw new InvalidDataException($errors);
}
}
}
My User entity:
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
* #ORM\Table(name="`user`")
* #UniqueEntity(fields="email", errorPath="email", message="user.email.unique")
* #UniqueEntity(fields="username", errorPath="username", message="user.username.unique")
*/
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private ?int $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
* #JMS\Type("string")
* #JMS\Groups({"api"})
* #Assert\NotBlank(message="user.email.not_blank")
* #Assert\Email(message="user.email.email")
*/
private string $email;
/**
* #var string The hashed password
* #ORM\Column(type="string")
* #JMS\Type("string")
* #Assert\NotBlank(message="user.password.not_blank")
* #Assert\Length(min=8, minMessage="user.password.length.min")
*/
private string $password;
/**
* #JMS\Type("string")
* #Assert\NotBlank(message="user.confirm_password.not_blank")
* #Assert\EqualTo(propertyPath="password", message="user.confirm_password.equal_to")
*/
private string $confirmPassword;
...
...
I have this error :
1) App\Tests\Service\Validator\ValidatorServiceTest::testValidatorForUser
Failed asserting that exception of type "App\Exception\InvalidDataException" is thrown.
How can I test if an exception thows or not ?
SOLVED:
The problem was with my $contraintViolationMock which always returned empty data.
I have to retrieve violations before and test that it's match with the mock violations. I think, it's the more simple solution instead of creating manually a ConstraintViolationList.
If you have a better solution, i'll take it.
public function testValidatorForUser()
{
$user = new User();
$user->setEmail('test');
$validator = self::$container->get(ValidatorInterface::class);
$violations = $validator->validate($user);
$validatorMock = $this->createMock(ValidatorInterface::class);
$validatorMock->expects($this->once())
->method('validate')
->willReturn($violations);
$this->expectException(InvalidDataException::class);
$validatorService = new ValidatorService($validatorMock);
$validatorService->validate($user);
}

Symfony Doctrine return empty values

I use Symfony doctrine to set and get data from my MySQL database. I can push new data without any problem but when I try to get them with a findAll for exemple, I get an array with the good length but nothing in.
Here's my controller:
namespace KGN\CoreBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\EntityManagerInterface;
use KGN\CoreBundle\Entity\Appointment;
use KGN\CoreBundle\Entity\Testy;
class AdminController extends Controller
{
public function indexAction()
{
return $this->render('KGNCoreBundle:Admin:index.html.twig');
}
public function aptAction()
{
$rep = $this->getDoctrine()
->getRepository('KGNCoreBundle:Testy');
$testy = $rep->findAll();
// return new Response('This is for show : '. count($testy) );
return new JsonResponse($testy);
}
public function createAction()
{
$em = $this->getDoctrine()->getManager();
$testy = new Testy();
$testy->setTitre('Magnifique');
$testy->setName('Helicoptere');
$em->persist($testy);
$em->flush();
return new Response('This is for create');
}
}
and what I get on my view page
[{},{}]
And it's true that there is 2 elements in my SQL table.
( I have create my entity with php bin/console doctrine:generate:entity without edition stuff in the "Testy" class or rep )
Entity/Testy
namespace KGN\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Testy
*
* #ORM\Table(name="testy")
* #ORM\Entity(repositoryClass="KGN\CoreBundle\Repository\TestyRepository")
*/
class Testy
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=255)
*/
private $titre;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set titre
*
* #param string $titre
*
* #return Testy
*/
public function setTitre($titre)
{
$this->titre = $titre;
return $this;
}
/**
* Get titre
*
* #return string
*/
public function getTitre()
{
return $this->titre;
}
/**
* Set name
*
* #param string $name
*
* #return Testy
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
}
associed rep
namespace KGN\CoreBundle\Repository;
/**
* TestyRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class TestyRepository extends \Doctrine\ORM\EntityRepository
{
}
Hi The Function findAll Return the correct answer but its return as array of Objects
And JsonResponse can't desplay Object.
to fixe that you have to create a custom function in your repository that return An array exemple
public function getAll() {
$qb = $this->createQueryBuilder('u');
return $qb->getQuery()->getArrayResult();
}
$em = $this->getDoctrine()->getManager();
$records = $em->getRepository("KGNCoreBundle:Testy")->findAll();
Hope its help you

Symfony Serialize doctrine entity

I have a simple entity class:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="CompanyUserRepository")
* #ORM\Table(name="company_users")
*/
class CompanyUser
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=100)
*/
private $firstName;
/**
* #ORM\Column(type="string", length=100)
*/
private $lastName ;
/**
* #ORM\OneToMany(targetEntity="Score", mappedBy="user")
*/
private $scores;
/**
* Constructor
*/
public function __construct()
{
$this->scores = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName
*
* #param string $firstName
*
* #return CompanyUser
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set lastName
*
* #param string $lastName
*
* #return CompanyUser
*/
public function setLastName($lastName)
{
$this->lastName = $lastName;
return $this;
}
/**
* Get lastName
*
* #return string
*/
public function getLastName()
{
return $this->lastName;
}
/**
* Add score
*
* #param \AppBundle\Entity\Score $score
*
* #return CompanyUser
*/
public function addScore(\AppBundle\Entity\Score $score)
{
$this->scores[] = $score;
return $this;
}
/**
* Remove score
*
* #param \AppBundle\Entity\Score $score
*/
public function removeScore(\AppBundle\Entity\Score $score)
{
$this->scores->removeElement($score);
}
/**
* Get scores
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getScores()
{
return $this->scores;
}
}
The problem comes when I try to get all users and serialize them in json:
/**
* #Route("/api/getUsers", name="getUsers")
*/
public function getUsers(Request $request){
$users = $this->getDoctrine()
->getRepository('AppBundle:CompanyUser')
->findAll();
$serializer = $this->get('serializer');
$data = $serializer->serialize($users, 'json');
return new Response($users);
}
I get A circular reference has been detected (configured limit: 1).
When I remove the getScores getter everything works fine. I need to get only the id, firstName and lastName. Is there a way to not serialize the other objects?
Well, it is a common thing to handle with circular references when serializing entities with relations.
Solution no. 1: Implements serializable interface and make relations attributes are not serialize/unserialize (most cases, it is a valid solution)
Solution no. 2: The setCircularReferenceLimit() method of this normalizer sets the number of times it will serialize the same object before considering it a circular reference. Its default value is 1. So, before calling serialize() method, do this:
public function getUsers(Request $request){
$users = $this->getDoctrine()
->getRepository('AppBundle:CompanyUser')
->findAll();
$serializer = $this->get('serializer');
$serializer->setCircularReferenceLimit(2); // Change this with a proper value for your case
$data = $serializer->serialize($users, 'json');
return new Response($data);
}
**** UPDATE ****
As #Derek says in his comment, solution no. 2 can be invalid in some versions of Symfony. Then you can try to set a handler for circular references this way:
$encoder = new JsonEncoder();
$normalizer = new ObjectNormalizer();
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getName(); // Change this to a valid method of your object
});
$serializer = new Serializer(array($normalizer), array($encoder));
var_dump($serializer->serialize($org, 'json'));
This should return your entity value instead to iterate over relations.

Symfony 2 - Retrieve Entity with Json, return another Entity

I have a problem while json_encodeing a Entity.
public function jsonvoteAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('KorumAGBundle:AGVote')->findOneById($id);
$response = new Response(json_encode($entity, 200));
$response->headers->set('Content-Type',' application/json');
return $response;
}
This code returns me a the users entity
{"users":{"__isInitialized__":false,"id":null,"nickname":null,"pwd":null,"email":null,"firstname":null,"lastname":null,"poste":null,"addr1":null,"addr2":null,"pc":null,"country":null,"phone":null,"province":null,"acess":null,"site":null,"crew":null,"utilisateur":null}}
And when I var dymp my $entity, it returns both my AGVote and USers entity.
Here is my AGVote Entity
<?php
namespace Korum\AGBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Korum\AGBundle\Entity\AGVote
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class AGVote
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $question;
/**
* #ORM\Column(type="smallint")
*/
private $actif;
/**
* #ORM\ManyToOne(targetEntity="\Korum\KBundle\Entity\Users", cascade={"all"})
*/
public $users;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set question
* Nb : Only AG admin can set a question
* #param text $question
*/
public function setQuestion($question)
{
$this->question = $question;
}
/**
* Get question
*
* #return text
*/
public function getquestion()
{
return $this->question;
}
/**
* Set actif
*
* #param smallint $actif
*/
public function setActif($actif)
{
$this->actif = $actif;
}
/**
* Get actif
*
* #return smallint
*/
public function getActif()
{
return $this->actif;
}
/**
* Set Users
*
* #param Korum\KBundle\Entity\Province $Users
*/
public function setUsers(\Korum\KBundle\Entity\Users $users)
{
$this->users = $users;
}
/**
* Get Users
*
* #return Korum\KBundle\Entity\Users
*/
public function getUsers()
{
return $this->users;
}
}
Does anyone have an idea of what happened ?
I tried to install the JSMSerializerBundle but event with Metadata library at version 1.1.
When I want to clear my cache, it failed with error :
See :
JMSSerializerBundle Installation : Catchable Fatal Error: Argument 1 passed to JMSSerializerBundle\Twig\SerializerExtension::__construct()
By default, json_encode only uses public properties.
So it serialized the only public property of AGVote: $users. The content of $users was an instance of User; which public fields were serialized.
You could work around these by adding a toArray() method to your entities, and then doing json_encode($entity->toArray()), but i highly recommend you to have a look and use the JMSSerializedBundle.

Resources