Symfony 2 forwarding requests to another controller with changed content - symfony

I want to forward a request to another controller using something like this:
$controller_response = $this->forward( 'MyBundle:Clients:getClients' );
That works fine, but I need to updated the request to have different content, I can't work out what method I should be using, the following don't work:
$request->content->set('new content');
$request->set('content', 'new content');
$request->setContent('new content');
Is it even possible to do this? If not I could create a brand new request and add it in as an argument, I'd rather avoid doing that though if possible.

You can alter the Response content with the setContent method.
$response->setContent('<html>Hello</html>');
You can also alter the request
$request->request->set('key', 'value');
$request->query->set('key', 'value');

$request = Request::createFromGlobals();
$newContent = json_decode($request->getContent(), true);
$request->initialize($request->query->all(), $request->request->all(), $request->attributes->all(), $request->cookies->all(), $request->files->all(), $request->server->all(), $newContent);
Unfortunately, there doesn't seem to be a great way of doing it, but this worked for me.

Related

how can I set content of request symfony?

I generate manually request content of my form and want to set data like:
form[1][]=2&form[2][]=3&form[3][]=5&form[4][]=8&form[apply]=
The Symfony Request object has an getContent() method, but hasn't setContent().
How can I set content alternatively?
Found a solution which is dirty but seems to work for me. Just reinitialize the request.
$request->initialize(
$request->query,
$request->request,
$request->attributes,
$request->cookies,
$request->files,
$request->server,
'<your-content>'
);
Symfony 4 > version:
$request->initialize(
$request->query->all(),
$request->request->all(),
$request->attributes->all(),
$request->cookies->all(),
$request->files->all(),
$request->server->all(),
'your-content>'
);
Another option is via Request::create method.
Request::create(
'<uri>',
'<method>',
<parameters>,
<cookies>,
<files>,
<server>,
'<your-content>'
);
Source: HTTP foundation
Another dirty solution with usage of closures: https://www.php.net/manual/en/closure.call.php
$requestClosure = function() use ($decryptedJson) {
$this->content = $decryptedJson;
return $this;
};
$request = $requestClosure->call($request);
How this works:
in closure the $this becomes the request itself so it's possible to call private content,
calling return $this in closure returns the $request in context of which the call was made,

Symfony2 why does forward nest request objects?

When I do a forward() in a controller, I lose my route and route_parameters.
When I have ParentAction, that does a forward to ChildAction. In Childaction I do return $this->render('myTemplate.html.twig', array()); then the request attributes get nested!
So when the template gets rendered, instead of $request['attributes']['_route_parameters'] I get $request['attributes']['request']['attributes']['_route_parameters'].
Although in ChildAction, when I do a $this->getRequest(); the hierarchie is normal.
Is this a bug, or am I doing something wrong?
The reason is that symfony doesn't assume you have the same route parameters.
So when you forward you need to re-supply the route parameters required by the new route even if they are the same.
(Incidentally you must also provide any query parameters for the new route.)
public function indexAction($param)
{
return $this->forward(
'AppBundle\\Controller\\DefaultController::otherAction',
array("someOtherParam" => $param),
$request->query->all() // Causes query string params to be copied
);
}
// This route has a different parameter.
public function otherAction($someOtherParam) ...
A possible solution would be to pass your Request as second parameter when forwarding.
$response = $this->forward('MyBundle:MyController:myAction', array('request' => $request));
Also, as forward is just a shortcut for core Symfony2 functionality, this may probably help.
In my case following code helped:
$subRequest = $this->container->get('request')->duplicate(
array(),
null,
array('topicId' => $topicId,'_controller' => 'SomeBundle:Topic:close'));
return $this->container->get('http_kernel')
->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
"Topic" is TopicController and "close" is closeAction

Module field with feeds, module generating data

I have an issue with triming a field before it is saved. I wanted to use substr(), or regex() with preg_match(). I have built a Drupal 7 module, but it can't work at all. I have tried using the trim plugin in feeds tamper module, but it doesn't seem to work. The data I am using is from a feed from Google Alerts. I have posted this issue here.
This is what I have done so far, and I know my regular expression is wrong; I was trying to get it do anything, just to see if I could get it to work, but I am pretty lost on how to add this type of function to a Drupal module.
function sub_node_save() {
$url = $node->field_web_screenhot['und'][0]['url'];
$url = preg_match('~^(http|ftp)(s)?\:\/\/((([a-z0-9\-]*)(\.))+[a-z0-9]*)($|/.*$)~i',$url );
$node->field_web_screenhot['und'][0]['url'] =$url;
return ;
}
I used the Devel module to get the field.
If there's an easy way to use substr(), I would consider that or something else.
Basically, I just want to take the Google redirect off the URL, so it is just the basic URL to the web site.
Depending on your question and later comments, I'd suggesting using node_presave hook (http://api.drupal.org/api/drupal/modules!node!node.api.php/function/hook_node_presave/7) for this.
It's called before both insert (new) and update ops so you will need extra validations to prevent it from executing on node updates if you want.
<?php
function MYMODULE_node_presave($node) {
// check if nodetype is "mytype"
if ($node->type == 'mytype'){
// PHP's parse_url to get params set to an array.
$parts = parse_url($node->field_web_screenhot['und'][0]['url']);
// Now we explode the params by "&" to get the URL.
$queryParts = explode('&', $parts['query']);
$params = array();
foreach ($queryParts as $param) {
$item = explode('=', $param);
$params[$item[0]] = $item[1];
}
//valid_url validates the URL (duh!), urldecode() makes the URL an actual one with fixing "//" in http, q is from the URL you provided.
if (valid_url(urldecode($parms['q']))){
$node->field_web_screenhot['und'][0]['url'] = urldecode($parms['q']);
}
}
}

Symfony2 functional testing InvalidArgumentException: The current node list is empty

I get "InvalidArgumentException: The current node list is empty." running functional tests through PHPUnit. Here is test i wrote:
public function testAdd()
{
$client = static::createClientWithAuthentication('main');
$crawler = $client->request('GET', 'en/manage');
$send_button = $crawler->selectButton('submit');
$form = $send_button->form(array(
'PrCompany[email]' => 'test#example.ua',
'PrCompany[first_name]' => 'Anton',
'PrCompany[last_name]' => 'Tverdiuh',
'PrCompany[timezone]' => 'Europe/Amsterdam'
));
$form['PrCompany[companies][1]']->tick();
$client->submit($form);
$this->assertTrue($crawler->filter('html:contains("User is invited")')->count() > 0);
}
You can debug the problem by using var_dump($client->getResponse()->getContent());
Additionally, I think you should write this:
$crawler = $client->submit($form);
Otherwise you'll be testing the response of the first url, before form submission.
I was also struggling with this, and It appeared that the selectButton method triggered this error.
After reading up on the DOM Crawler docs I found that the selectButton method takes the actual button text as a string argument. So if your button is 'submit my form please', that will be your text.
It does take different parameters too, as shown below (taken from the docs)
A selectButton() method is available on the Crawler which returns another
Crawler that matches a button (input[type=submit], input[type=image],
or a button) with the given text.
EDIT
After finally successfully completing the test I would also recommend you follow this example for testing forms:
use Goutte\Client;
$client = new Client();
$crawler = $client->request('GET', 'https://github.com/login');
$form = $crawler->selectButton('Log in')->form();
$form['login'] = 'symfonyfan';
$form['password'] = 'anypass';
$crawler = $client->submit($form);
$this->assertTrue($crawler->filter('html:contains("Welcome Back")')->count() > 0);
The main difference being, I have used the Goutte bundle, which I installed with composer from the packagist (in my case I added "fabpot/goutte": "1.0.*#dev")
As a follow up to what #greg0ire wrote, check to see if
var_dump($client->getResponse()->getContent());
Returns a redirect page instead of the actual content. If so, you can add this:
$client->followRedirects(true);
I had the same problem with Silex application. I was looking for
$buttonCrawler = $crawler->selectButton('input[type="submit"]');
Instead, the correct way to do it is give the value of the button
$buttonCrawler = $crawler->selectButton('value_of_the_button');
For example, in your page:
<form>
...
<input type="submit" value="Click Me">
</form>
And in your tests:
$buttonCrawler = $crawler->selectButton('Click Me');
$form = $buttonCrawler->form();
...
I see question still dont have answer. I had the same problem.
In my case goutte was not able to do this request because input name is changed by javascript on the fly.
When goutte received html it saw one form. And when submitting with pre filled params, form input elements could not be matched by $form->setValues($params) so \InvalidArgumentException was thrown.
Solved by doing request by hand.
// $form->setValues($data);
// $this->getGoutte()->submit($form);
$data = array(
'input_name[key]' => 'value'
);
$this->getGoutte()->request($form->getMethod(), $form->getUri(), $params);
You can try to use Codeception with Symfony2 module. It provides flexible interface to Symfony2 functional tests and has better debugging features.
This error would occur when crawler can't find form element requested; Quite tricky when you are using, for instance, form builder as when run, it will create different input name:
$form = $this-> createFormBuilder($store)
->add('storeNumber','text')
->add('storeName','text')
->add('save', 'submit')
->getForm();
will output field name like:
form_storeNumber
which should be used in test class:
$form=$crawler->selectButton('save')->form();
$form['form_storeNumber']='10';

How do I create a functional test which includes a POST to a page with parameters?

I've read the documentation from Symfony2 but it doesn't seem to work. Here's my functional test:
public function testSearch()
{
$client = static::createClient();
$crawler = $client->request('POST', '/search/results', array('term' => 'Carteron'));
$this->assertTrue($crawler->filter('html:contains("Carteron")')->count() > 0);
$this->assertTrue($crawler->filter('html:contains("Auctions")')->count() > 0);
}
In my controller the "term" parameter is null when this request comes in. However, search works just fine when I perform it on the site, so I know its a problem with setting up the test.
I had the same problem, neither $request->query nor $request->request worked. Both did the same for me: it returned $default, not the given $parameters (Symfony 2.3).
Same behaviour as chris, in normal web browsers it works. I fixed this by replacing:
public function searchResultsAction($name)
{
$request = Request::createFromGlobals();
$term = trim($request->request->get('term'));
return $this->searchResultsWithTermAction($term);
}
With:
public function searchResultsAction(Request $request, $name)
{
// do NOT overwrite $request, we got it as parameter
//$request = Request::createFromGlobals();
$term = trim($request->request->get('term'));
return $this->searchResultsWithTermAction($term);
}
So createFromGlobals() does only work if $_GET and $_POST are set. Shich is not the case if you use the symfony test client. you have to add the $request parameter to the action.
(I used $name as a GET parameter in my routing)
I've never gotten an answer to this, but have implemented a work around that seems to work for my testing purposes. I'd love to hear feedback or get a real answers to this questions.
What I've done is create a 2nd route for the purposes of testing.
In real usage the uri would be /search/results?term=searchtermhere
For the purposes of testing, this didn't work. I could never get access to the term value when invoked via the automated test.
So what I've done is create a 2nd route just for testing which has a uri of /search/results/{searchtermhere}.
Then my action class used for a real search would call down to another function and pass the term to the function:
public function searchResultsAction()
{
$request = Request::createFromGlobals();
$term = trim($request->request->get('term'));
return $this->searchResultsWithTermAction($term);
}
So my functional tests would excersize searchResultsWithTermAction(), so the only code coverage I'm missing from this workaround is the extraction of the term from the request.
I don't know if anyone is still searching for an answer to this, but basically the problem is that you are looking in the wrong place for the parameter.
For your example you would need:
$term = trim($request->query->get('term'));
Hope this helps!

Resources