I have a symfony website, and Im trying to do some unit testing. I have this kind of test where I try to submit something:
<?php
namespace Acme\AcmeBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class HomeControllerTest extends WebTestCase {
public function testrandomeThings() {
$client = static::createClient();
$crawler = $client->request(
'POST',
'/',
array(
"shopNumber" => 0099,
"cardNumber" => 231,
"cardPIN" => "adasd"),
array(),
array());
}
but I dont think that the data Im sending is being received in the controler:
class HomeController extends Controller
{
public function indexAction()
{
var_dump($_POST);
die;
return $this->render('AcmeBundle:Home:index.html.twig');
}
}
the var_dump is actually returning me an empty array.
What am I missing to send information through my POST request?
$_POST is a variable filled by PHP and the symfony request is only created from this globals if called directly over http. The symfony crawler doesn't make a real request, it creates a request from the parameters supplied in your $client->request and executes it. You need to access this stuff via the Request object. Never use $_POST, $_GET, etc. directly.
use Symfony\Component\HttpFoundation\Request;
class HomeController extends CoralBaseController
{
public function indexAction(Request $request)
{
var_dump($request->request->all());
die;
return $this->render('CoralWalletBundle:Home:index.html.twig');
}
}
use $request->request->all() to get all POST parameters in an array. To get only a specific parameter you can use $request->request->get('my_param'). If you ever need to acces GET parameters you can use $request->query->get('my_param'), but better set query parameters already in the routing pattern.
I think you're trying to do this:
$client = static::createClient();
$client->request($method, $url, [], [], [], json_encode($content));
$this->assertEquals(
200,
$client->getResponse()
->getStatusCode()
);
You're putting your data (content) in as the params array but you want to put it in as the raw body content which is a JSON encoded string.
Related
Currently we're doing unit testing in Laravel, and I just noticed my colleague this line below (its working fine though). I look for a documentation in Laravel about this but I can't find it. It seems all we're just focusing on getting the request input values in the documentation.
use Illuminate\Http\Request;
// ...more code here
$request = Request::create('/users/all', 'GET');
I just wanna ask how to pass a parameter using the above code line? And someone can give me a documentation about that.
Check the create function at here:
https://github.com/symfony/symfony/blob/5cfe73d95419bac1ffdddc4603db7266e428b454/src/Symfony/Component/HttpFoundation/Request.php#L336
As you can see, you can pass parameteres as third argument:
Example:
Request::create('/users/all', 'GET', ['username' => 'admin']);
Note: Laravel Request extends Symfony Base Request class
The 3rd argument to create is for an array of parameters. Illuminate\Http\Request extends Symfony\Component\HttpFoundation\Request which defines the create method:
public static function create(string $uri, string $method = 'GET', array $parameters = [], array $cookies = [], array $files = [], array $server = [], $content = null)
I am looking for a way to return a twig view, wait 5 seconds and then send the user to an external URL.
When I visit the URL, I only see a white page as the template doesn't get an opportunity to load.
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class RedirectController extends AbstractController
{
public function index(Request $request)
{
// TO DO - check if url is valid
$url = $request->get('url');
$this->redirectAway($url);
return $this->render('base/redirect.html.twig', ['url' => $url]);
}
public function redirectAway($url)
{
$response = new Response();
$response->setStatusCode(200);
$response->headers->set('Refresh', '5; url=https://' . $url);
$response->send();
return $response;
}
}
Any help would be most appreciated! <3 I am trying to implement a redirect notice for users for external URLs.
Your redirectAway method creates a Response object and returns it to the caller.
Your index method calls redirectAway, and ignores its return value. Instead, of calling response->send, you should pass the response object as the 3rd parameter in render:
return $this->render(
'base/redirect.html.twig',
['url' => $url],
$this->redirectAway()
);
I'm using Woocommerce webhooks to listen out for every time an order is created/updated/deleted.
I've setup the webhook in Woocommerce as follows
In my Laravel routes file I have set up the route as follows:
use Illuminate\Http\Request;
// API routes...
Route::post('api/v1/orders/create', function (Request $request) {
Log::debug($request->all());
return $request->all();
});
However, when I view the logs as well as the return data in POSTMAN, all I get is an empty array.
Any HTTP method other than 'GET' throws a MethodNotAllowedException
I'm not sure of any other way in Laravel to consume data other than with Request $request.
According to my understanding of routing in Laravel, the input that you pass in to the function is meant to actually be variables for your route.
So if you had a route in your API:
api/v1/orders/{id}/create then in the route function you'd pass in id as the method argument. So this would be correct:
Route::post('api/v1/orders/{id}/create', function ($id) {
Log::debug($id);
return $id;
});
It's looking for request in your route definition.
Rather create a controller. Then in your routes.php use this:
Route::post('api/v1/orders/create', 'OrdersController#create')
That tells your routing to redirect all HTTP POST calls to api/v1/orders/create to the OrdersController.php and the create() method within that controller.
In your controller, you'll be able to use the $request variable as an input argument and it should work.
So in OrdersController.php:
class OrdersController extends Controller {
public function create(Request $request) {
Log::debug($request->all());
return $request->all();
}
}
Good Luck!
This worked for me. My route in api.php is as follow.
Route::post('/woocommerce/webhook/', 'Api\WoocommerceController#test');
And my controller action is as follow.
public function test()
{
$payload = #file_get_contents('php://input');
$payload = json_decode( $payload, true);
\Log::info(json_encode( $payload));
return response()->json([ 'data' => $payload, 'status' => \Symfony\Component\HttpFoundation\Response::HTTP_OK]);
}
Debugger shows, that there is POST data in request, but I cannot get it by $request->get('foo');
And $request->request->all(); gives array [0].
My Ajax POST request (by AngularJS):
...
$http.post('http://admin/about-company/setJSONObj',
{foo: 'bar'}
);
My controller with debugging info in comments (Symfony 3.2.9):
use Symfony\Component\HttpFoundation\Request;
...
public function updateAction(Request $request)
{
$foo = $request->get('foo'); // null
$requestType = $request->getContentType(); // json
$content = $request->getContent(); // {"foo":"bar"}
I used these approach on Symfony 2.7 project, and it worked fine, but I'm not sure, what's happening in these case?
Also, maybe there is any Symfony framework config variable that tells not to parse POST data, or hides it during caching the request?
For POST request is:
$request->request->get('foo');
Try using FormData for your client-side ajax's call
For example : try something like
var formData = new FormData();
formData.append('foo', 'bar')
$http.post('http://url',
formData
);
Ok, I wasn't paying attention that you used json so,
You wont get the content of $foo in request but you need to json_decode the $content
So by keeping the same way you sent data :
$http.post('http://admin/about-company/setJSONObj',
{foo: 'bar'}
);
You just have to call
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
...
/**
* #Route("/setJSONObj", name="admin_pages_set_ajax_obj")
* #Method("POST")
*/
public function updateAction(Request $request)
{
$foo = $request->get('foo'); // null
$requestType = $request->getContentType(); // json
$content = $request->getContent(); // {"foo":"bar"}
$data = json_decode($content, true);
dump($data['foo']); // "foo"
//And you can know replace the data of the request. So
$request->request->replace($data);
My guess is because symfony need have the 'Content-Type' 'application/x-www-form-urlencoded' but Angular by default have a application/json (so you can override the headers on the $http.post call.
It turned out, my mistake was to expect POST variables in $request->request->all(); while posting JSON: see explanation https://www.toptal.com/....
For creating REST api with Symfony it's normal to use FOSRestBundle: see body listener. So, it solves my problem in elegant way.
Also, #henrily suggested a workaroud somebody can use, but it's just a workaround.
I'm using Laravel to create a JSON REST API, and it has been quite present so far. However I'm needing to wrap my JSON outputs with a bit of meta status JSON created by say a metaDataController (or probably model) and I am curious what a good approach to this might be.
For instance, all responses would take on the following format:
{
"meta": {
"status": 200,
"notifications": 2
},
"response": {
//JSON from the route's Controller/Model/etc
}
}
From what I can tell I either need to modify the Laravel Response defaults and delegate to a metaDataController, or create some sort of Route::any that merges the two sections of JSON as mentioned in Returning Multiple Laravel Eloquent Models as JSON. Although I always know metaDataController, the other controller is in flux depending on the route.
I'm thinking there must be a way to declare this structure as a default for all routes or a Route::group.
Thanks!
I don't think doing json_decode->json_encode cycle is an acceptable solution (as in Chris answer).
Here is another solution
use Illuminate\Http\Response;
use Illuminate\Http\Request;
Route::filter('apisuccess', function($route, Request $request, Response $response = null) {
$response->setContent(json_encode([
'data' => $response->original,
'meta' => ['somedata': 'value']
]));
});
Then I would attach this filter to my REST API routes.
Edit: another solution (more complex).
Create a custom Response class:
use Illuminate\Http\Response;
class ApiResponse extends Response
{
protected $meta;
protected $data;
public function __construct($content = '', $status = 200, $headers = array())
{
parent::__construct([], $status, $headers);
$this->meta = [];
}
public function withMeta($property, $value = null)
{
if (is_array($property))
$this->meta = $property;
else
array_set($this->meta, $property, $value);
return $this;
}
public function withData($data)
{
$this->data = $data;
return $this;
}
public function sendContent()
{
echo json_encode(['success' => true, 'data' => $this->data, 'meta' => $this->meta, 'echo' => ob_get_contents()]);
}
}
Put it as a singleton in IOC container:
$this->app->bindShared('ApiResponse', function() {
return new \Truinject\Http\ApiResponse();
});
Finally, create filter and use it as "before" on your routes:
Route::filter('apiprepare', function(Illuminate\Routing\Route $route, Illuminate\Http\Request $request) {
$data = $route->run();
return App::make('ApiResponse')->withData($data);
});
So we are basically overriding default response class with our own, but still calling the appropriate controller with $route->run() to get the data.
To set meta data, in your controller do something like this:
\App::make('ApiResponse')->withMeta($property, $value);
I've added method "meta" in my base API controller class, which encapsulates this.
You could use the global after filter in app.php to catch all responses, then reconfigure it however you please:
App::after(function($request, $response)
{
if(is_a($response, 'Illuminate\Http\JsonResponse')) {
$response->setContent(json_encode(array(
'data' => json_decode($response->getContent()),
'foo' => 'bar',
'cat' => 'dog'
)));
}
});
In the above example, you're taking all the existing json data and putting it in a child data element (this would be "response" in your example) then adding foo and bar. So foo, bar and data would be top level json objects.
If you don't like the global positioning, after is an event sent, so you could also listen to it inside a controller/elsewhere.