Symfony2 - Doctrine Save entites with one to one relation - php

I want to save a form that I have created but I have an error.
I have 3 entities:
class T {
/**
* #ORM\OneToOne(targetEntity="MyNameSpace\ProfileBundle\Entity\Person", cascade={"persist"})
*/
private $information;
}
class Person {
/**
* #ORM\OneToOne(targetEntity="MyNameSpace\MediaBundle\Entity\Document", cascade={"persist"}))
*/
private $photo_profile;
}
class Document
{
private $file;
}
When I save my "T" class with this code :
public function createAction()
{
$entity = new T();
$request = $this->getRequest();
$form = $this->createForm(new TType(), $entity);
$form->bindRequest($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getEntityManager();
$em->persist($entity);
$em->flush();
}
}
I have this error:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (namespace.person, CONSTRAINT FK_3370D440809EFCB0 FOREIGN KEY (photo_profile_id) REFERENCES Document (id))
Any helps will be cool
Thank you for all
Here the code I have:
/**
* Creates a new Tutor entity.
*/
public function createAction()
{
$entity = new T();
$person = new Person();
$document = new Document();
$person->setPhoto($document);
$entity->setInformation($person);
$request = $this->getRequest();
$form = $this->createForm(new TType(), $entity);
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($entity->getInformation()->getPhoto());
$em->persist($entity->getInformation());
$em->flush();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('Index'));
}
and I have this error:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (db.T, CONSTRAINT FK_58C6694C2EF03101 FOREIGN KEY (information_id) REFERENCES Person (id))
Any help please will be cool
Thanks

I ran into this issue as well and cascading didn't help so I ended up just using oneToMany, manyToOne relations instead.
The alternative solution I had was to not apply the foreign key constraints in the generated SQL but that would remove my --force ability (and force me to write my own script to ignore the FK constraints), so the oneToMany solution was more elegant for me.

You are trying to save a Person but you did not specify the relation to Document (which must be named photo_profile) as nullable, hence this error.

I had the same issue with a ManyToOne relation;. I just had to :
persist the owner entity
flush
create the inverse and bind it
persist and flush again
Then everything went normal !
Well I can't explain that but I just share my experience and maybe it will serve !

Related

Symfony forms & handling Doctrine constraint violations gracefully

I have built a form using symfony's form builder to create new entries with the doctrine entity manager, but upon creating new entries, I occasionally run into a Integrity constraint violation: 1062 Duplicate entry'. I would like to properly handle this exception using the DRY principles, what is the best way to do this gracefully?
Preferably I would have a warning show even before submitting but after submission would be acceptable also.
FlightController.php
/**
* #Route(path="/new", name="flight_new", methods={"GET", "POST"})
*/
public function new(Request $request)
{
//Create New Flight
$flight = new Flight($this->getDoctrine()->getManager());
$form = $this->createForm(FlightType::class, $flight);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$flight = $form->getData();
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($flight);
$entityManager->flush();
return $this->redirectToRoute("flight_list");
}
return $this->render('flight/new.html.twig', array('form' => $form->createView()));
}
Flight.php The constraint is a compound key:
/**
* #ORM\Entity(repositoryClass="App\Repository\FlightRepository")
* #ORM\Table(name="flight",
* uniqueConstraints={#ORM\UniqueConstraint(name="flight",columns={"ap_from","ap_to"})}
* )
*/
class Flight
{
Thanks in advance

Symfony2 form methods diffrent in Prod and Dev version

I have problem with my project.
My problem exist when I try to delete some data from entity. My controller was generated with Sesio generator. Here is my code:
/**
* Deletes
* #Route("/{id}/delete", name="delete")
* #Method({"DELETE"})
*/
public function deleteAction(Request $request, Task $task) {
$form = $this->createDeleteForm($task);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($task);
$em->flush();
$this->get('session')->getFlashBag()->add('notice_success', 'Success');
} else {
$this->get('session')->getFlashBag()->add('notice_error', 'NO DELETE');
}
return $this->redirectToRoute('task');
}
/**
* Creates a form to delete.
*/
private function createDeleteForm(Task $task) {
return $this->createFormBuilder()
->setAction($this->generateUrl('delete', array('id' => $task->getId())))
->add('submit', \Symfony\Component\Form\Extension\Core\Type\SubmitType::class, array('label' => 'Delete'))
->getForm()
;
}
I have to tell you that this code work nice on DEV (app_dev.php) but It isn't working in PROD version.
I try to solve that problem and I have tried to change form method to POST and it work property od PROD and DEV. It look like DELETE method doesnt work in PROD version.
Someone have similar problem?
If you're using the AppCache, the kernel will ignore the _method parameter added for DELETE method.
To solve the problem in your web/app.php call Request::enableHttpMethodParameterOverride(); before creating the Request object:
...
$kernel = new AppCache($kernel);
Request::enableHttpMethodParameterOverride();// <-- add this line
$request = Request::createFromGlobals();
...
See http://symfony.com/doc/current/form/action_method.html and http://symfony.com/doc/current/reference/configuration/framework.html#configuration-framework-http-method-override

Symfony manually validate entity with relation

I want to validate a User entity with custom constraint & validator. So far it's working when triggered by form workflow, but if I trigger it manually, I loose one relation I setup before calling validation :
UserController :
$user = new User();
$user->setRoles($roles);
$user->setSite($site);
...
$violations = $this->container->get('validator')->validate($user);
User entity with Site relation :
/**
* #var Site the site linked to the entity
* #ORM\ManyToOne(targetEntity="LCH\MultisiteBundle\Entity\Site", cascade={"all"})
* #ORM\JoinColumn(name="site_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $site;
Validator :
public function validate($user, Constraint $constraint)
{
$email = $user->getEmail();
// $site var is null while other "direct fields are filled
$site = $user->getSite();
$roles = $user->getRoles();
$username = $user->getUsername();
How can I manually validate this entity using preceeding set relation?
My problem found its origin in Symfony2 form validation structure. : as $form->handleRequest($request) indeed validates form, all hooked validators (groups, custom constrains and callbacks) are triggered.
My $site was null because validator was fired long before I set up my $user->site attribute...
// sumbit in this method trigger validation too early in my needs
$form->handleRequest($request);
$em = $this->getDoctrine()->getManager();
if ($form->isSubmitted() && $form->isValid() && !$isAjax) {
// custom processes to decide what to create
...
// Here is the user creation
$user->setRoles($roles);
$user->setSite($site);
...
// And the check
$violations = $this->container->get('validator')->validate($user);
}
The solution here lied in disabling validation groups in the main type. Doing so, $user is fully passed to validator.
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => false,
));
}
The main drawback with this system is that I have to manually trigger validation for this very type in all interactions, but this type is complex enough to make sense here.

Symfony entity validation won't report violation

I am trying to add some validation to my entity, using the Symfony validation component, i have added some constraints to my User Entity.
/**
* #param ClassMetadata $metadata
*/
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('username', new Assert\NotBlank);
$metadata->addPropertyConstraint('password', new Assert\NotBlank);
$metadata->addPropertyConstraint('first_name', new Assert\NotBlank);
$metadata->addPropertyConstraint('last_name', new Assert\NotBlank);
}
Now i want to test if i get some errors, when violating the constraints, this is done like this.
$user = new User();
$user->username = '';
$user->password = '';
$validator = Validation::createValidator();
if (0 < count($validator->validate($user))) {
throw new \RuntimeException('The given user is invalid');
}
But the count is zero, which is odd, as all the constraints is clearly violated? Am i missing something here? Well i must be :D.
Might be worth to notice, that my application is not a Symfony application; it's a ordinary php application, i am just using the component.
You need to specify which methods act as mapping methods:
$validator = Validation::createValidatorBuilder()
->addMethodMapping('loadValidatorMetadata')
->getValidator()

Symfony2: How to modify the current user's entity using a form?

I am trying to add a simple form to allow my users to edit their profile. My problem is:
Since the entity "linked" to the form is the same as the current user object ($user === $entity, see below), if the form validation fails, then the view is rendered with the modified user object (ie. with values of the non-valid form).
Here my (classic) controller:
public function profileAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$entity = $em->getRepository('AcmeSecurityBundle:User')->find($user->id);
// $user === $entity => true
$form = $this->createForm(new ProfileType(), $entity);
$request = $this->getRequest();
if ($request->getMethod() === 'POST')
{
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('profile'));
}
}
return $this->render('AcmeSecurityBundle:User:profile.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
So I wondered how to have two distincts objects $user and $entity. I used clone() and it worked well for the view rendering part (the $user object was not modified), but it created a new record in database instead of updating the old one.
PS: I know I should use FOSUserBundle. But I would really like to understand my mistake here :)
I used the same solution as FOSUserBundle, which is calling $em->refresh() on my entity when the form validation fails:
public function profileAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$entity = $em->getRepository('AcmeSecurityBundle:User')->find($user->id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$form = $this->createForm(new ProfileType(), $entity);
$request = $this->getRequest();
if ($request->getMethod() === 'POST')
{
$form->bindRequest($request);
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('profile'));
}
$em->refresh($user); // Add this line
}
return $this->render('AcmeSecurityBundle:User:profile.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Note that if you are using what is called a "virtual" field in "How to handle File Uploads with Doctrine" (in my case "picture_file" you will need to clear it by hand:
$em->refresh($user);
$user->picture_file = null; // here
One approach would be to always redirect:
if ($form->isValid()) {
$em->persist($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('profile'));
Of course you lose error messages and changes.
Another approach would be to define an entity manager just for your UserProvider. $user would no longer be the same as $entity. Bit of extra overhead but it certainly makes the problem go and would prevent similar interactions with other forms that might modify all or part of an user entity.
In a similar fashion, you reduce the overhead by creating an entity manager just for your profile form. With this method, the overhead would only be incurred when editing the profile.
Finally, you could ask yourself if it really mattered that the display data was not quite right in this particular case. Would it really bother anything? Would anyone notice but you?
See How to work with Multiple Entity Managers in Symfony Cookbook
Another idea is to clone your user entity in your user provider. This will divorce it from the entity manager.
You could also use $entityManager->detach($user); to remove the user from the entity manager.
And why is the token user an entity anyways? Consider making a completely independent User class with minimal information pulled from the database by your user provider. That is what I do.

Categories