Handle different method request in slim framework 3 - slim

Hey i am learning slim framework 3 it may be a newbie question,
in the documentation i see a way to kind of group the request using map() like this
$app->map(['GET', 'POST'], '/books', function ($request, $response, $args) {
// Create new book or list all books
});
How do i handle each request, say i wanna handle GET request where do i put the logic in my controller?
i have tried to work around it using this :
//category routes
$app->group('/category', function(){
$this->get('', 'CategoryController:getCategories');
$this->post('', 'CategoryController:insertCategory');
});
and it worked just fine, but still how do i handle it ?

When the GET and POST handling is different, I would prefer your working approach.
With one route for multiple methods you need to manually check for POST/GET (when something different should be done).
$app->map(['GET', 'POST'], '/books', function ($request, $response, $args) {
$method = $request->getMethod();
switch($method) {
case 'POST':
// insert category
break;
case 'GET':
// get categories
break;
default:
return $response->write('Could not handle request');
}
});

Related

what are the differences beetween notifyUrl response and completePurchase response?

i'm try to use omnipay/paypal, i use this code for returnUrl page:
public function completePayment(Request $request)
{
//return 'pagina dopo acquisto';
$gateway = Omnipay::create('PayPal_Express');
$gateway->setUsername('blastor_89-facilitator_api1.msn.com');
$gateway->setPassword('BEWB2BEW9CHCV3EQ');
$gateway->setSignature('AFcWxV21C7fd0v3bYYYRCpSSRl31AC5Dp4AnVYBnMIkNFxSQTj8h.lqD');
$gateway->setTestMode(true);
$params = session()->get('params');
$response = $gateway->completePurchase($params)->send();
$paypalResponse = $response->getData();
//$this->store($paypalResponse);
if(isset($paypalResponse['PAYMENTINFO_0_ACK']) && $paypalResponse['PAYMENTINFO_0_ACK'] === 'Success') {
// here you process the response. Save to database ...
}
else {
// Failed transaction ...
}
}
it response with $paypalResponse, while if i use notifyUrl page what do notifyUrl response? what are the differences?
These two responses are completely different although they contain the same basic idea.
The complete purchase response has the data from your call to completePurchase(). The notify response has data that is fed into your application from a notify (PayPal IPN) call. I suggest that you dump the data from each one to take a look.

Send two params in GET request

I'm pretty new using vertx framework, and in the documentation I cannot see the silly thing about how to send two parameters in a GET request. So far I tried this.
$.getJSON('/user/'+ attributeName + ":"+value, function (data) {
userListData = data;
$.each(data, function () {
$('#userInfoName').text(data.fullname);
$('#userInfoAge').text(data.age);
$('#userInfoGender').text(data.gender);
$('#userInfoLocation').text(data.location);
});
});
And then in server side
router.get("/user/:attributeName:value").handler(routingContext -> {
JsonObject query = new JsonObject();
query.put(routingContext.request().getParam("attributeName"), routingContext.request().getParam("value"));
But then I can see how attributeName not only gets the value of the first param but part of the second, very weird.
You're probably doing it wrong. You can get it as a single param and later split with ":" or have two parameters defined in the url as... /:attribname/:value/...
Both will handle your requirement

jsonp and zendframe work 1 doesnt return any thing(im working with phonegap and use Zend as a service)

this is my zend controller
and this is how i called my service
please teach me how. it's important ! thank you so much
Seems like a bit of extra work with what you have there. I've done it this way in both ZF1 and ZF2:
PHP
// notice 'searchteams' is lowercased. There's been problems in the past
// when camelcasing action names, by default I believe ZF is looking for
// a lowercase action name unless you've configured it otherwise
public function searchteamsAction()
{
// make sure this is an ajax request (this is a method I usually write, if
// Zend has one you could use that here)
if ($this->isXmlHttpRequest())
{
// instantiate model, get the results, good
// disabling view and layout, good
// headers, never really had an issue that needed the headers to be set here
// would simply have..
echo json_encode($res);
}
}
JS: in the success method in the AJAX call, I'd do this:
success : function (data){
// parse the json
var parsed_data = $.parseJSON(data);
console.log(parsed_data);
// do stuff with parsed_data
}
Hope that helps.
Check your datas with a return var_dump($res); before $this->_response->setHeader(...
if you have datas, try to add $this->_response->setHeader('Content-Type', 'application/json', true);
If not work, try to replace
$this->_response->setHeader(...);
$this->_response->setHeader(...);
$this->_response->setHeader(...);
$this->_response->setHeader(...);
and echo Zend_Json::encode($res);
by only
return $this->_helper->json($res);
You can also see the error on the side of jQuery with something like:
error: function(xhr, status, error) {
alert("error status: " + status);
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
}
I hope it will help you :)

PHPUnit: Testing form submissions with session variables stored in Symfony2

I had a small test done in PHP for a Controller I had written in Symfony2:
class DepositControllerTest extends WebTestCase {
public function testDepositSucceeds() {
$this->crawler = self::$client->request(
'POST',
'/deposit',
array( "amount" => 23),
array(),
array()
);
$this->assertEquals(
"Deposit Confirmation",
$this->crawler->filter("title")->text());
}
}
Up to here, everything was great. Problem started when I realized I wanted to disable possible re-submissions while refreshing the page. So I added a small mechanism to send nonce on every submission.
It works something like this:
class ReplayManager {
public function getNonce() {
$uid = $this->getRandomUID();
$this->session->set("nonce", $uid);
return $uid;
}
public function checkNonce($cnonce) {
$nonce = $this->session->get("nonce");
if ($cnonce !== $nonce)
return false;
$this->session->set("nonce", null);
return true;
}
}
So I had to mofidy the controller to get the nonce when displaying the form, and consume it when submitting.
But now this introduces a problem. I cant make a request to POST /deposit because I dont know what nonce to send. I thought to requesting first GET /deposit to render the form, and setting one, to use it in the POST, but I suspect Symfony2 sessions are not working in PHPUnit.
How could I solve this issue? I would not want to go to Selenium tests, since they are significant slower, not to mention that I would have to rewrite A LOT of tests.
UPDATE: I add a very simplified version of the controller code by request.
class DepositController extends Controller{
public function formAction(Request $request){
$this->replayManager = $this->getReplayManager();
$context["nonce"] = $this->replayManager->getNonce();
return $this->renderTemplate("form.twig", $context);
}
protected function depositAction(){
$this->replayManager = $this->getReplayManager();
$nonce = $_POST["nonce"];
if (!$this->replayManager->checkNonce($nonce))
return $this->renderErrorTemplate("Nonce expired!");
deposit($_POST["amount"]);
return $this->renderTemplate('confirmation.twig');
}
protected function getSession() {
$session = $this->get('session');
$session->start();
return $session;
}
protected function getReplayManager() {
return new ReplayManager($this->getSession());
}
}
I'm not sure what ReplayManager does, but it looks to me as if it is not the right class to handle the 'nonce'. As the 'nonce' is ultimately stored in and retrieved from the session it should either be handled by the controller or abstracted out into its own class which is then passed in as a dependency. This will allow you to mock the nonce (sounds like a sitcom!) for testing.
In my experience problems in testing are actually problems with code design and should be considered a smell. In this case your problem stems from handling the nonce in the wrong place. A quick refactoring session should solve your testing problems.
It is possible to access the Symfony2 session from PHPUnit via the WebTestCase client. I think something like this should work:
public function testDepositSucceeds() {
$this->crawler = self::$client->request(
'GET',
'/deposit',
);
$session = $this->client->getContainer()->get('session');
$nonce = $session->get('nonce');
$this->crawler = self::$client->request(
'POST',
'/deposit',
array("amount" => 23, "nonce" => $nonce),
array(),
array()
);
$this->assertEquals(
"Deposit Confirmation",
$this->crawler->filter("title")->text());
}
EDIT:
Alternatively, if there is a problem getting the nonce value from the session, you could try replacing the two lines between the GET and POST requests above with:
$form = $crawler->selectButton('submit');
$nonce = $form->get('nonce')->getValue(); // replace 'nonce' with the actual name of the element

What's the best way to handle a REST API's 'create' response in Backbone.js

I'm using backbone.js to interact with a REST API that, when posting to it to create a new resource, responds with a status of 201, a 'Location' header pointing to the resource's URI, but an empty body.
When I create a new model at the moment, its successful, but the local representation of the model only contains the properties I explicitly set, not any of the properties that would be set on the server (created_date, etc.)
From what I understand, Backbone would update its representation of the model with data in the body, if there were any. But, since there isn't, it doesn't.
So, clearly, I need to use the location in the Location header to update the model, but what's the best way to do this.
My current mindset is that I would have to parse the url from the header, split out the id, set the id for the model, then tell the model to fetch().
This seems really messy. Is there a cleaner way to do it?
I have some influence over the API. Is the best solution to try to get the API author to return the new model as the body of the response (keeping the 201 and the location header as well)?
Thanks!
Sounds like you will have to do a little customization.
Perhaps override the parse method and url method of your model class inherited from
Backbone.Model.
The inherited functions are:
url : function() {
var base = getUrl(this.collection);
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
},
parse : function(resp) {
return resp;
},
and you could try something like:
parse: function(resp, xhr) {
this._url = xhr.getResponseHeader('location')
return resp
}
url: function() {
return this._url
}
Yes, backbone.js really wants the result of a save (be it PUT or POST) to be a parseable body which can be used to update the model. If, as you say, you have influence over the API, you should see if you can arrange for the content body to contain the resource attributes.
As you point out, its makes little sense to make a second over-the-wire call to fully materialize the model.
It may be that a status code of 200 is more appropriate. Purists may believe that a 201 status code implies only a location is returned and not the entity. Clearly, that doesn't make sense in this case.
With Backbone 0.9.9, I couldn't get the accepted answer to work. The signature of the parse function seems to have changed in an older version, and the xhr object is no longer available in the function signature.
This is an example of what I did, to make it work with Backbone v0.9.9 and jQuery 1.8.3 (using a Deferred Object/Promise), relying on the jqXHR object returned by Backbone.Model.save() :
window.CompanyView = Backbone.View.extend({
// ... omitted other functions...
// Invoked on a form submit
createCompany: function(event) {
event.preventDefault();
// Store a reference to the model for use in the promise
var model = this.model;
// Backbone.Model.save returns a jqXHR object
var xhr = model.save();
xhr.done(function(resp, status, xhr) {
if (!model.get("id") && status == "success" && xhr.status == 201) {
var location = xhr.getResponseHeader("location");
if (location) {
// The REST API sends back a Location header of format http://foo/rest/companys/id
// Split and obtain the last fragment
var fragments = location.split("/");
var id = fragments[fragments.length - 1];
// Set the id attribute of the Backbone model. This also updates the id property
model.set("id", id);
app.navigate('companys/' + model.id, {trigger: true});
}
}
});
}
});
I did not use the success callback that could be specified in the options hash provided to the Backbone.Model.save function, since that callback is invoked before the XHR response is received. That is, it is pointless to store a reference to the jqXHR object and use it in the success callback, since the jqXHR would not contain any response headers (yet) when the callback is invoked.
Another other to solve this would be to write a custom Backbone.sync implementation, but I didn't prefer this approach.