Symfony forms & handling Doctrine constraint violations gracefully - php

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

Related

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.

Secure method using annotations

I have a page with a form and want to know if it is possible to access it using GET, but only allow logged in users to POST to it.
I know this can be done in security.yml, but am not sure how to do it with annotations.
/**
* #param Request $request
* #return Response
* #Security("has_role('ROLE_USER')")
* #Method(methods={"POST"})
*/
public function calculatorAction(Request $request)
{
$form=$this->createForm(new CallRequestType(),$callReq=new CallRequest());
$form->handleRequest($request);
if($form->isValid()){
//blabla
}
return $this->render('MyBundle:Pages:calculator.html.twig', array('form' => $form));
}
This will secure the whole function, but I want to access it, just not POST to it without being logged in. An alternative would be to check if there is a logged in user in the $form->isValid() bracket. But I'm still wondering if it can be done with annotations.
You could do something like this.
You can allow both method types anonymously, and check just inside the controller to see if the user is authenticated and is POSTing.
(You don't state which version of symfony you're using, so you might have to substitute the authorization_checker (2.8) for the older security.context service)
/**
* #param Request $request
* #return Response
*
* #Route("/someroute", name="something")
* #Method(methods={"POST", "GET"})
*/
public function calculatorAction(Request $request)
{
if ( !$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY') && $request->getMethod() == 'POST') {
throw new AccessDeniedHttpException();
}
$form=$this->createForm(new CallRequestType(),$callReq=new CallRequest());
$form->handleRequest($request);
// you also need to check submitted or youll fire the validation on every run through.
if($form->isSubmitted() && $form->isValid()){
//blabla
}
return $this->render('MyBundle:Pages:calculator.html.twig', array('form' => $form));
}

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 - Doctrine Save entites with one to one relation

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 !

Categories