Symfony / phpUnit : functional test of a form modifying an entity - php

How would you do a functional test (not unit tests) of a form binded to an entity ?
Context
Let's say you have an entity "Car", with a field "id" and another field "numberPlate", and a page to edit data about a car.
CarController.php :
//...
public function imsiDetailsChangeAction(Request $request)
{
$car_id = $request->get('car_id');
$car = $this->getDoctrine()->getRepository('ClnGsmBundle:Car')->Find($car_id);
if ($simCard != null)
{
$form = $this->createForm(new CarType()), $car);
if($request->isMethod('POST'))
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->flush();
return $this->redirect($this->generateUrl('car_view', array('car_id' => $car->getId())));
}
}
}
else
{
throw new NotFoundHttpException();
}
return $this->render('SiteBundle:Car:carEdit.html.twig', array('car' => $car, 'form' => $form->createView()));
}
//...
What I want
A test using phpUnit doing the following :
create a Car entity with the numberPlate "QWE-456"
load the page with the form
using the crawler, replace the numberPlate with "AZE-123" in the form, and submit the form
assert that my car entity's numberPlate now equals "AZE-123"
What I tried
(just in case: my own code is a bit different, here is what I would do with the car example)
CarControllerTest.php :
//...
public function SetUp()
{
//start kernel, stores entity manager in $this->em and client in $this->client
}
//...
public function testEditForm()
{
$car = new Car();
$car->setNumberPlate("QWE-456");
$this->entityManager->persist($simCard);
$this->em->flush();
$crawler = $this->client->request('GET', '/fr/Car/edit/'.$car->getId());
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$formNode = $crawler->filterXpath("//div[#id='main']//form");
$form = $formNode->form(array(
'car[plateNumber]'=>'AZE-123',
));
//var_dump($car->getPlateNumber());
$this->client->submit($form);
//var_dump($car->getPlateNumber());
$this->assertEquals('AZE-123',$car->getPlateNumber);
}
I expect this test to pass, and the second var_dump to print "AZE-123" instead of "QWE-456". But my entity isn't modified.
How should I do this ?

You should refresh the data reloading it from the database: the refresh method do it for you, so try this:
$this->client->submit($form);
$this->em->refresh($car);
$this->assertEquals('AZE-123',$car->getPlateNumber);
I suggest you to check before the HTTP Response in order to verify the correct interaction, as example:
$response = $this->client->getResponse();
$this->assertTrue($response->isRedirection());
Hope this help

Related

Adding related entities better way

How to implement adding related Role entities better way? It would be nice to keep it inside of a form if possible or maybe if I add a method to an User entity directly... Is there built-in mechanism for it or what patter shall I use?
public function add(Request $request)
{
$data = $request->request->all();
$user = new User();
$form = $this->createForm(UserType::class, $user);
$form->submit($data);
if (false === $form->isValid()) {
return $form;
}
$user = $form->getData();
// Adding a Roles
if (!empty($data['user_roles'])) {
foreach ($data['user_roles'] as $value) {
// Checking if a Role is present in DB
$role = $this->getDoctrine()
->getRepository(Role::class)
->findOneBy(['role_name' => $value]);
if ($role) {
$user->setUserRole($role);
}
}
}
$this->em->persist($user);
$this->em->flush();
return new JsonResponse(
[
'status' => 'ok',
'last_insert_id' => $user->getId(),
],
JsonResponse::HTTP_CREATED
);
}
I've always worked like that and I've never had a problem. In fact, I don't even use symfony's forms because they're not that flexible. The best way to do it is the one that you feel more comfortable with. Just be sure to be clean and consistent.
Of course you can add a method to the User entity, but it's just a matter of modularization, that's not very necessary in this case.

Populating form data from entity in Symfony2

I have an action that basically renders a form and I want it to be a new form if the ID is null and an edit form if the ID matches with the PK in the DB. Obviously my logic is wrong because a new form is rendering every single time. .
public function editGlobalFirewallFilter(Request $request, Entities\GlobalFirewallFilter $firewall_rule = null) {
n
// Check if we have a valid rule. If not create a new blank one and associate our account id
// if( ! $firewall_rule ) {
// $results = $this->getDoctrine()->getRepository('bundle:GlobalFirewallFilter');
// $rules = $results->findAll();
// $firewall_rule = new Entities\GlobalFirewallFilter();
// }
$firewall_rule = new Entities\GlobalFirewallFilter();
// Generate our form
$form = $this->createForm(new SAForms\GlobalFirewallRuleType(), $firewall_rule);
$form->handleRequest($request);
if($form->isValid()) {
// Save our firewall rule
$em = $this->getDoctrine()->getManager();
$em->persist($firewall_rule);
$em->flush();
return $this->redirect($this->generateUrl('_dashboard__global_firewall'));
}
return array(
'title' => $firewall_rule->getFirewallFilterId() ? 'Edit Rule' : 'New Rule',
'form' => $form->createView(),
);
}
You should use the form generator command to be oriented in the right way :
Generating a CRUD Controller Based on a Doctrine Entity
http://symfony.com/doc/current/bundles/SensioGeneratorBundle/commands/generate_doctrine_crud.html
use this command :
php app/console generate:doctrine:crud
I will generate the skeleton of you controller with all the standard actions as wanted, in your specifica case, updateAction, newAction, and editAction.
I am not quite sure why are there results and rules - you don't use them. I think this code should do the trick.
public function editGlobalFirewallFilter(Request $request, Entities\GlobalFirewallFilter $firewall_rule = null) {
// Check if we have a valid rule. If not create a new blank one and associate our account id
$firewall_rule = $firewall_rule ?: new Entities\GlobalFirewallFilter();
// Generate our form
$form = $this->createForm(new SAForms\GlobalFirewallRuleType(), $firewall_rule);
$form->handleRequest($request);
if($form->isValid()) {
// Save our firewall rule
$em = $this->getDoctrine()->getManager();
$em->persist($firewall_rule);
$em->flush();
return $this->redirect($this->generateUrl('_dashboard__global_firewall'));
}
return array(
'title' => $firewall_rule->getFirewallFilterId() ? 'Edit Rule' : 'New Rule',
'form' => $form->createView(),
);
}
P.S. Sadly I can't comment yet.. Can you provide controller actions where you use this function?

Can't get doctrine-generated form to update entity

I've created an entity that I can't update using doctrine-generated form. Here's an action:
public function executeEdit(sfWebRequest $request)
{
$this->forward404Unless($id = $request->getParameter('id'), "Required parameter 'id' must be present in request");
$this->forward404Unless($cart = Doctrine::getTable('ShoppingCart')->find($id), sprintf("No object could be retrieved by %s", $id));
$this->id = $id;
$this->form = new ShoppingCartForm($cart);
if($request->isMethod(sfRequest::POST)) {
$this->form->bind($request->getParameter('shopping_cart'));
if ($this->form->isValid())
{
$cart = $this->form->save();
$this->redirect(link_to('cart/show?id='.$cart->getId()));
}
}
}
Form's isNew() method returns false, but the sf_method is set to PUT. When I use sfRequest::PUT doctrine tries to add new entity with that id. It seems like it shouldn't behave like that so what I am doing wrong?

Entity persists even when form has error

I have an issue where I have a form type that persists the associated entity even when the form is not valid.
I have confirmed that the form indeed has errors via $form->getErrorsAsString(). I have also confirmed that the logical if statement that checks if the form is valid or not comes out false. The entity still persists despite the fact that the form is never valid.
I'm not sure what I'm doing wrong here as I have no other spot that I can find that either persists the entity or flushes the entity manager. Here's my controller:
/**
* #Route("/settings/profile", name="settings_profile")
* #Template();
*/
public function profileAction()
{
$user = $this->getUser();
$profile = $user->getUserProfile();
if (null === $profile) {
$profile = new UserProfile();
$profile->setUser($user);
$profileDataModel = $profile;
} else {
$profileDataModel = $this->getDoctrine()->getManager()->find('MyAppBundle:UserProfile',$profile->getId());
}
$form = $this->createForm(new ProfileType(),$profileDataModel);
$request = $this->getRequest();
if ($request->getMethod() === 'POST') {
$form->bind($request);
if ($form->isValid()) {
// This logic never gets executed!
$em = $this->getDoctrine()->getManager();
$profile = $form->getData();
$em->persist($profile);
$em->flush();
$this->get('session')->setFlash('profile_saved', 'Your profile was saved.');
return $this->redirect($this->generateUrl('settings_profile'));
}
}
return array(
'form' => $form->createView(),
);
}
I must have a listener or something somewhere that is persisting the user.
My work around for this temporarily is to do:
$em = $this->getDoctrine()->getManager()
if ($form->isValid()) {
// persist
} else {
$em->clear();
}
Until I can ferret out what listener or other data transformer is causing this.

Add data to a submitted form object inside a controller in Symfony2

I want to save some data where part of it is from the user where they submit it through a form and the other part is generated in the actual controller. So something like:
# controller
use Acme\SomeBundle\Entity\Variant;
use Acme\SomeBundle\Form\Type\VariantType;
public function saveAction()
{
$request = $this->getRequest();
// adding the data from user submitted from
$form = $this->createForm(new VariantType());
$form->bindRequest($request);
// how do I add this data to the form object for validation etc
$foo = "Some value from the controller";
$bar = array(1,2,3,4);
/* $form-> ...something... -> setFoo($foo); ?? */
if ($form->isValid()) {
$data = $form->getData();
// my service layer that does the writing to the DB
$myService = $this->get('acme_some.service.variant');
$result = $myService->persist($data);
}
}
How do I get $foo and $bar into the $form object so that I can validate it and persist it?
Here's the general pattern I'm using:
public function createAction(Request $request)
{
$entity = new Entity();
$form = $this->createForm(new EntityType(), $entity);
if ($request->getMethod() == 'POST') {
$foo = "Some value from the controller";
$bar = array(1, 2, 3, 4);
$entity->setFoo($foo);
$entity->setBar($bar);
$form->bindRequest($request);
if ($form->isValid()) {
$this->get('some.service')->save($entity);
// redirect
}
}
// render the template with the form
}
Reading the code for the bind method of the Form class, we can read this:
// Hook to change content of the data bound by the browser
$event = new FilterDataEvent($this, $clientData);
$this->dispatcher->dispatch(FormEvents::BIND_CLIENT_DATA, $event);
$clientData = $event->getData()
So I guess you could use this hook to add your two fields.

Categories