How do I test subdomain based routes in Symfony2 - symfony

I have a app which uses subdomains to route to agencies:
foo.domain.dev -> Agency:showAction(foo)
bar.domain.dev -> Agency:showAction(bar)
domain.dev -> Agency:indexAction()
These each correspond to an Agency entity and controller.
I have a listener that listens for the onDomainParse event and writes the subdomain into the request attributes.
/**
* Listens for on domainParse event
* Writes to request attributes
*/
class SubdomainListener {
public function onDomainParse(Event $event)
{
$request = $event->getRequest();
$session = $request->getSession();
// Split the host name into tokens
$tokens = $this->tokenizeHost($request->getHost());
if (isset($tokens['subdomain'])){
$request->attributes->set('_subdomain',$tokens['subdomain']);
}
}
//...
}
I then use this in the controller to reroute to a show action:
class AgencyController extends Controller
{
/**
* Lists all Agency entities.
*
*/
public function indexAction()
{
// We reroute to show action here.
$subdomain = $this->getRequest()
->attributes
->get('_subdomain');
if ($subdomain)
return $this->showAction($subdomain);
$em = $this->getDoctrine()->getEntityManager();
$agencies = $em->getRepository('NordRvisnCoreBundle:Agency')->findAll();
return $this->render('NordRvisnCoreBundle:Agency:index.html.twig', array(
'agencies' => $agencies
));
}
// ...
}
My question is:
How do i fake this when doing a test with WebTestCase?

Visit the subdomain by overriding HTTP headers for the request and test for the right page:
Untested, may contain errors
class AgencyControllerTest extends WebTestCase
{
public function testShowFoo()
{
$client = static::createClient();
$crawler = $client->request('GET', '/', array(), array(), array(
'HTTP_HOST' => 'foo.domain.dev',
'HTTP_USER_AGENT' => 'Symfony/2.0',
));
$this->assertGreaterThan(0, $crawler->filter('html:contains("Text of foo domain")')->count());
}
}

Based on the Symfony docs on host-based routes, Testing your Controllers:
$crawler = $client->request(
'GET',
'/',
array(),
array(),
array('HTTP_HOST' => 'foo.domain.dev')
);
If you don't want to pad all your requests with array parameters, this might be better:
$client->setServerParameter('HTTP_HOST', 'foo.domain.dev');
$crawler = $client->request('GET', '/');
...
$crawler2 = $client->request('GET', /foo'); // still sends the HTTP_HOST
There's also a setServerParameters() method on the client, if you have a few parameters to change.

Related

How to load 'isSubmitted()-method' (defined in service controller) into the main controller?

I've put a registration form in a method so, that I can use it in different places.
My service registration controller looks like this:
public function loadRegisterForm()
{
$user = new User();
$form = $this->createForm(RegistrationType::class, $user);
$form->handleRequest($this->request);
$errors = "";
if ($form->isSubmitted())
{
if ($form->isValid())
{
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
$user->setIsActive(1);
$user->setLastname('none');
$user->setCountry('none');
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
}
else
{
$errors = $this->get('validator')->validate($form);
}
}
$parametersArray['form'] = $form;
$parametersArray['errors'] = $errors;
return $parametersArray;
}
services.yml looks like this:
register_form_service:
class: ImmoBundle\Controller\Security\RegistrationController
calls:
- [setContainer, ["#service_container"]]
And the main controller where I load the service controller:
private function indexAction()
{
/**
* Load register form
*/
$registerForm = $this->get('register_form_service');
$registerParameters = $registerForm->loadRegisterForm();
$registerParameters['form']->handleRequest($request);
return $this->render(
'ImmoBundle::Pages/mainPage.html.twig',
array(
'register_form' => $registerParameters['form']->createView(),
'errors' => $registerParameters['errors'],
)
);
}
The form itself is well rendered, so there is no problem. However nothing happens if I try to submit the form. I know that I should add the following line to the main controller
if ($registerParameters['form']->isSubmitted())
{
// add to db
}
But is there any way to do it only in a service controller?
You do not need a service definition to inject the container into your controller. If the controller extends Symfony\Bundle\FrameworkBundle\Controller\Controller all services are accesible via ->get(). Next to that, $form->isValid() already checks whether the form is submitted.
Why is your action private? It should be public, and it need to get the Request object as it's first parameter:
public function indexAction(Request $request)
{
$user = new User();
$form = $this->createForm(RegistrationType::class, $user);
$form->handleRequest($request);
if ($form->isValid()) {
// Do something here
}
}
See http://symfony.com/doc/current/book/forms.html#handling-form-submissions

How can i get the user with id session in param request with symfony2

I'm trying to do a little API with Symfony2.
I send a session id to my controller with a URL like this:
localhost/symfony2/web/app_dev.php/users/getuser/c5auv7mrp45rnd046cfv0vgl96
Then, in Symfony,
/**
* #Route("/getuser/{sessionId}")
*/
public function getSessionAction(Request $request, $sessionId)
{
// Here is what i'm trying to do
$packJson = array(
'user_id' => $userid
);
$response = new JsonResponse();
$response->setData($packJson);
return $response;
}
So, i would like to retrieve my user Id only with the sessionId argument.
Of course, it will be load from Db
I don't understand the logic between Session object and User Objet
Thanks
I think you want to use a token to identify a user. That means you have one token for each user in your database. If that is correct then it has nothing to do with sessions or a session-object.
you could simple retrieve your user with:
/**
* #Route("/getuser/{token}")
*/
public function getSessionAction($token)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AdminBundle:User')->findOneBy(array('token' => $token);
$response = new JsonResponse();
if (!$entity) {
$response->setData('error' => 'bad token');
return $response;
}
$packJson = array(
'user_id' => $entity->getId()
);
$response->setData($packJson);
return $response;
}

Symfony2 Unit Testing a service

I'm still very new to symfony and really enjoying it.
I'm at the stage where I've managed to create and setup a service, the service itself uses 2 dependencies:
A Data API that returns json data (this is a separate library which
i have implemented as a service and came with its own unit tests).
The Doctrine Entity Manager.
The service pulls the data required using the api and then loops through the data and checks to see if the data already exists, if it does it updates the existing entity and persists it, Otherwise it creates a new entity assigns the data and persists it.
I now need to write a unit test for this, i've not used PHPUnit only from symfony2 tutorials which were testing responses from a controller.
How do i go about writing a unit test for this service?
In particular mocking the data that i would normally pull from the api.
and then checks to see if the entry needs to be updated or created?
A code example would be really helpful so i can then use this as a template to create tests for other similar services that i create.
Here's the Service i want to test:
<?php
namespace FantasyPro\DataBundle\DataManager;
use Doctrine\ORM\EntityManager;
use FantasyDataAPI\Client;
use FantasyPro\DataBundle\Entity\Stadium;
class StadiumParser {
/**
* #var EntityManager $em
*/
private $em;
/**
* #var Client $client
*/
private $client;
public function __construct( EntityManager $em, Client $client) {
$this->em = $em;
$this->client = $client;
}
/**
* #return array
*/
public Function parseData(){
//var_dump($this);
$stadiumData = $this->client->Stadiums();
//var_dump($stadiumData);
//get the Repo
$repo = $this->em->getRepository('DataBundle:Stadium');
$log = array();
foreach ($stadiumData as $stadium) {
// Get the current stadium in the list from the database
$criteria = array( 'stadiumID' => $stadium['StadiumID'] );
$currentStadium = $repo->FindOneBy( $criteria );
if ( ! $currentStadium) {
$currentStadium = new Stadium(); //no stadium with the StadiumID exists so create a new stadium
$logData = [
'action' => 'Added Stadium',
'itemID' => $stadium['StadiumID'],
'itemName' => $stadium['Name']
];
$log[] = $logData;
} else {
$logData = [
'action' => 'Updated Stadium',
'itemID' => $stadium['StadiumID'],
'itemName' => $stadium['Name']
];
$log[] = $logData;
}
$currentStadium->setStadiumID( $stadium['StadiumID'] );
$currentStadium->setName( $stadium['Name'] );
$currentStadium->setCity( $stadium['City'] );
$currentStadium->setState( $stadium['State'] );
$currentStadium->setCountry( $stadium['Country'] );
$currentStadium->setCapacity( $stadium['Capacity'] );
$currentStadium->setPlayingSurface( $stadium['PlayingSurface'] );
$this->em->persist( $currentStadium );
}
$this->em->flush();
return $log;
}
}
****** UPDATE *******
after reading ilpaijin's answer.
I've simplified the service so it no longer returns a log, i initially had this in so i could check what had been added by sending the log to a twig template in my controller, I eventually plan to have this running as a command so i can run a it via a cron job so the log bit is unnecessary.
I'm now setting the entity within my construct as i couldn't work out how to pass an entity as a injected dependency.
Now grabbing a new entity by using createNewStadium() method.
The Updated Service:
namespace FantasyPro\DataBundle\DataManager;
use Doctrine\ORM\EntityManager;
use FantasyDataAPI\Client;
use FantasyPro\DataBundle\Entity\Stadium;
class StadiumParser {
/**
* #var EntityManager $em
*/
private $em;
/**
* #var Client $client
*/
private $client;
/**
* #var Stadium Stadium
*/
private $stadium;
public function __construct( EntityManager $em, Client $client) {
$this->em = $em;
$this->client = $client;
}
/**
* Gets a list of stadiums using $this->client->Stadiums.
* loops through returned stadiums and persists them
* when loop has finished flush them to the db
*/
public Function parseData(){
$data = $this->client->Stadiums();
//get the Repo
$repo = $this->em->getRepository('DataBundle:Stadium');
foreach ($data as $item) {
// Get the current stadium in the list
$criteria = array( 'stadiumID' => $item['StadiumID'] );
$currentStadium = $repo->FindOneBy( $criteria );
if ( ! $currentStadium) {
$currentStadium = $this->createNewStadium; //no stadium with the StadiumID use the new stadium entity
}
$currentStadium->setStadiumID( $item['StadiumID'] );
$currentStadium->setName( $item['Name'] );
$currentStadium->setCity( $item['City'] );
$currentStadium->setState( $item['State'] );
$currentStadium->setCountry( $item['Country'] );
$currentStadium->setCapacity( $item['Capacity'] );
$currentStadium->setPlayingSurface( $item['PlayingSurface'] );
$this->em->persist( $currentStadium );
}
$this->em->flush();
}
// Adding this new method gives you the ability to mock this dependency when testing
private function createNewStadium()
{
return new Stadium();
}
}
What you basically need is Unit Testing the service using what is called "Test doubles".
This means you should mock the dependencies your service has, this way you are able to test only the service in isolation without really relying on the deps but only on a mocked version of them, with hardcoded values or behaviour.
A true example based on your real implementation isn't possible since you have tight coupled deps as $currentStadium = new Stadium();. You should pass deps like these in a constructor or via getter/setter in order to be able to mock it when Unit testing.
Once done it a very indicative example would be:
// class StadiumParser revisited and simplified
class StadiumParser
{
private $client;
public function __construct(Client $client)
{
$this->client = $client;
}
public function parseData()
{
$stadiumData = $this->client->Stadiums();
// do something with the repo
$log = array();
foreach ($stadiumData as $stadium) {
$logData = [
'action' => 'Added Stadium',
'itemID' => $stadium['StadiumID'],
'itemName' => $stadium['Name']
];
$log[] = $logData;
}
// do something else with Doctrine
return $log;
}
}
and the test
// StadiumParser Unit Test
class StadiumParserTest extends PHPUnit_Framework_TestCase
{
public function testItParseDataAndReturnTheLog()
{
$client = $this->getMock('FantasyDataAPI\Client');
// since you class is returning a log array, we mock it here
$expectedLog = array(
array(
'action' => 'Added Stadium',
'itemID' => $stadium['StadiumID'],
'itemName' => $stadium['Name']
)
);
// this is the mocked or test double part.
// We only need this method return something without really calling it
// So we mock it and we hardcode the expected return value
$stadiumData = array(
array(
"StadiumID" => 1,
"Name" => "aStadiumName"
)
);
$client->expects($this->once())
->method('Stadiums')
->will($this->returnValue($stadiumData));
$stadiumParser = new StadiumParser($client);
$this->assertEquals($expectedLog, $stadiumParser->parseData());
}
}
I voluntarily omitted the EntityManager part because I guess you should have a look at the Symfony Doc relative to how to unit test code interacting with the database
-----EDIT2-----
Yes he was right, you shouldn't. One possible way that comes in mind is to extract the creation of the entity in a protected/private method. Something like:
// class StadiumParser
public Function parseData()
{
...
foreach ($stadiumData as $stadium) {
...
if ( ! $currentStadium) {
$currentStadium = $this->createNewStadium();
...
}
// Adding this new method gives you the ability to mock this dependency when testing
private function createNewStadium()
{
return new Stadium();
}
-----EDIT3-----
I want to suggest you another approach. This should be, probably, a better choice should the Stadium entity needed in different services or different part of the same. What I'm proposing is called Builder pattern but a Factory could also be an option here. Browse a bit for their differences.
As you can see this extract some code from the method, distribute better the responsibility between the classes and leaves all cleaner and easier to read for you and your teammates. And you already know how to mock it when testing.
class StadiumParser
{
private $stadiumBuilder;
...
public function __construct( StadiumBuilder $builder, ...) {
$this->stadiumBuilder = $stadiumBuilder;
...
}
public Function parseData()
{
...
foreach ($stadiumData as $stadium) {
...
$currentStadium = $repo->FindOneBy( $criteria );
if ( ! $currentStadium) {
$currentStadium = $this->stadiumBuilder->build($currentStadium, $stadium);
}
$this->em->persist($currentStadium);
...
somewhere you have this new Builder that return a Stadium instance. this way your StadiumParser service is not coupled anymore with the entity, but the StadiumBuilder is it. The logic is something like:
// StadiumBuilder class
namespace ???
use FantasyPro\DataBundle\Entity\Stadium;
class StadiumBuilder
{
// depending on the needs but this should also has a different name
// like buildBasic or buildFull or buildBlaBlaBla or buildTest
public function build($currentStadium = null, $stadium)
{
if (!$currentStadium) {
$currentStadium = new Stadium();
}
$currentStadium->setStadiumID( $stadium['StadiumID'] );
$currentStadium->setName( $stadium['Name'] );
$currentStadium->setCity( $stadium['City'] );
$currentStadium->setState( $stadium['State'] );
$currentStadium->setCountry( $stadium['Country'] );
$currentStadium->setCapacity( $stadium['Capacity'] );
$currentStadium->setPlayingSurface( $stadium['PlayingSurface'] );
return $currentStadium;
}
}

Best way to manage filer form and result page

What's the best way to manage filter page and result page in Symfony?
I have a controller that manage filter form and execute query. The result of this query must pass in another controller action. The result must show in another controller action because I used knp_paginator. If I render the result in same controller action of filter form, when change page the controller show filter form and not result.
this is the approach that I used:
Action for create find form:
public function findAction(Request $request)
{
$form = $this->createFindForm($request);
$form->handleRequest($request);
if(($form->isSubmitted() && !$request->isXmlHttpRequest()))
{
if(($this->isValidFindForm($form) && $form->isValid()))
{
$parm = $request->request->get('findForm');
return $this->redirect($this->generateUrl('list_documents',$parm));
}
}
return $this->render(
'myBundle:Documents:document\document_find.html.twig',
array('form' => $form->createView())
);
}
private function createFindForm(Request $request)
{
$form = $this->createForm(
new findDocumentType(
$this->getDoctrine()->getManager(),
$request
),
null,
array(
'action' => $this->generateUrl('find_documents'),
'method' => 'POST',
)
);
return $form;
}
I used $parm = $request->request->get('findForm'); to get the querystring. "findForm" is the name of my filter form.
The redirecting action :
public function listAction(Request $request)
{
$documents = $this->searchDocument($request);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$documents,
$this->get('request')->query->get('page', 1)
);
return $this->render(
'myBundle:Documents:document\document_list.html.twig',
array('pagination' => $pagination)
);
}
The request is passed to search function (request contains querystring)
In search action:
private function searchDocument(Request $request)
{
$parm = $request->query;
$repository = $this->getDoctrine()->getRepository('docliteBundle:Document\\document');
$query = $repository
->createQueryBuilder('d');
....
With $request->query->get() I have access to all parameter.
Hope this help someone.
P.S. for the pagination I used KNP_paginator

Symfony unit testing & the container

When unit testing in Symfony 2, the controller I'm testing does not receive the service container causing the test to fail with the good old Call to a member function get() on a non-object
I can't use $this->forward from the testing controller as it also does not have the service container.
I found this reference but it seems as though I would be using it for the wrong reason, has anyone had any experience with this?
http://symfony.com/doc/current/book/testing.html#accessing-the-container
Edit: Here is my test:
<?php
namespace HvH\ClientsBundle\Tests\Controller;
use HvH\ClientsBundle\Controller\ClientsController;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\HeaderBag;
use Symfony\Component\HttpFoundation\Session;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ClientsControllerTest extends WebTestCase
{
public function testGetClientsAction()
{
$client = static::createClient();
$container = $client->getContainer();
$session = $container->get('session');
$session->set('key', 'value');
$session->save();
$request = new Request;
$request->create('/clients/123456', 'GET', array(), array(), array(), array(), '');
$headers['X-Requested-With'] = "XMLHttpRequest";
$request->headers->add($headers);
/* This doesn't work */
/*
$controller = new Controller;
$status = $controller->forward( 'HvHClientsBundle:Clients:getClients', array('request' => $request) );
*/
$clients_controller = new ClientsController();
$status = $clients_controller->getClientsAction($request);
$this->assertEquals(200, $status);
}
}
Here is the part of the clients controller where it fails
<?php
namespace HvH\ClientsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use HvH\APIschemaBundle\Controller\Validation;
//FOSRestBundle
use FOS\RestBundle\View\View;
class ClientsController extends Controller
{
//Query all clients
public function getClientsAction(Request $request)
{
$request_type = $request->headers->get('X-Requested-With');
if($request_type != 'XMLHttpRequest') {
return $this->render('HvHDashboardBundle:Dashboard:dashboard.html.twig' );
}
//get any query strings
$query_strings = $request->query->all();
$definition = $this->get("service.handler")->definition_handler(__CLASS__, __FUNCTION__);
//once data has been prepared
return $this->get('fos_rest.view_handler')->handle($view);
}
}
I think the reason the controller isn't getting a container is because you are trying to instantiate and interact with it directly rather than simulating requests using the client (See the section on Functional Tests in the Testing section of the Symfony2 book).
You need something more like this (not sure if the route is correct):
public function testGetClientsAction()
{
$client = static::createClient();
$client->request(
'GET', '/clients/123456',
array(), /* request params */
array(), /* files */
array('X-Requested-With' => "XMLHttpRequest"),
);
$this->assertEquals(200, $client->getResponse()->getStatusCode());
}
Note also, the request() method returns a crawler instance which provides helper methods to verify the content of the response if required.

Resources