RequestStack when should I use getMasterRequest rather than getCurrentRequest? - php

Since 2.4, we can inject the RequestStack in our services.
To retrieve the current Request, there is two methods:
/** #var Request */
$request = $requestStack->getCurrentRequest();
// OR
/** #var Request */
$request = $requestStack->getMasterRequest();
When should we use the currentRequest ?
And when should we use the masterRequest?
For example, if I need to retrieve the parameters in the body of the current Request, I would use:
$params = $request->request->all();
Which of the two methods should I use ?

When dealing with multiple requests, the non master request comes from internal requests. Usually from twig in the form of
{{ render(controller('foo')) }}
When the second request is created, only the request format and locale are copied to the new instance. Therefore, any data you would expect to come from the user, will not be available. This includes request parameters, ip address, and any HTTP headers.
In most situation, you will only need to deal with the current request. But, to answer your question, any time you need data coming from the user, you will require the master request. An example would be using the users IP address to pre-select a CountryType form control.
When using getMasterRequest()->getClientIp() you would be given the IP address of the user (something like 209.58.130.174). When calling it on the non master you will be given the localhost/loopback IP (127.0.0.1). There aren't really any methods that will return null, they simply return different information about how the request was created.

The master request is the one that comes from the original user; the subrequest is the one that you do internally — either with the forward() method of HttpKernel — or by the forward() helper of the framework's Controller class — or {% render ... %} in Twig.
https://stackoverflow.com/a/12456998/1078488
This means you would normally use the current request, if you don't explicitely want the actual request made by the user.
I've used a service for injecting the current request:
app.request:
class: Symfony\Component\HttpFoundation\RequestStack
factory:
- '#request_stack'
- 'getCurrentRequest'
Then you can inject it like this:
app.some_service:
class: AppBundle\Service\SomeService
arguments:
- '#app.request'
- '#some_other_service'

Related

Route to URL helper default parameters

I am using the code found here https://laracasts.com/discuss/channels/laravel/how-to-prefixing-in-routes-for-localization to prefix my routes with a locale.
Route::prefix('{lang?}')->middleware('locale')->group(function() {
Route::get('/', function () {
return view('index');
})->name('index');
});
This locale is optional, but then my question is how am I supposed to reinject the locale when calling the route helper.
route('index');
to generate: /it/ or /, or another, depending on the current locale.
If have tried this with no success :
Route::resourceParameters(['lang' => 'en']);
Actually, I'm a bit unsure about what the question is. I took it as a way to generate URL without setting its dynamic parameters using route helper based on #rahulsm's answer.
So, I just figured out that you can set default parameters to UrlGenerator class. By listening on container url and request rebind event, it's possible to set default parameters there.
Inside AppServiceProvider boot
$this->app->rebinding('url', function ($url, $app) {
$url->defaults(['lang' => $app->getLocale()]);
});
$this->app->rebinding('request', function ($app) {
$app['url']->defaults(['lang' => $app->getLocale()]);
});
And then tried to call it,
route('index') // http://test.dev/en (based on config)
route('index', ['lang' => 'what']) //http://test.dev/what
This was tested only on Laravel 5.5, but I'm sure would also working on Laravel 5.4.
To explain a bit more about rebinding method available Laravel container[1], firstly it's good to know how is Laravel request lifecycle works. In a one line simplified words, it should be the same as how an application in general works, which is: receive a request, do the logic, return a response.
At the second part, appear stage commonly said as bootstrapping, which one of its logic is try to store (register) classes (services) that mostly are important for the application[2] to work. Into a container. Thus, intended to be shared or even just for easily to be called, whether for the high-end developers or the framework itself.
Once registered, it will then be booted. This booting process has several actions to call[3]. One of it that suit this case is firing (local) events. Once a service has been resolved, it will try to call all registered rebound functions (listeners) at this stage. I can only assume this purpose is to make the application can be "easily mutate" the currently-resolving instance (service). Thus, defining rebinding method to listen to recalled event is the way to go.
Since Laravel allows to re-resolve (re-instantiate) a service, which means our previous stored value in the class is lost[4], waiting it to be resolved (which then the listener called) is much make sense, right?
Back to rebinding snippet above, I used to listen to both url and request rebound services because url is request's dependent. It waits for request service to be fully resolved and then call setRequest[5] method which flushes the needed instance which is Illuminate\Routing\RouteUrlGenerator that holds default parameter.
And as the name implies, defaults, used to set the default named parameters used by the URL generator[6].
cit
[1] Container here refer to both Illuminate\Foundation\Application also Illuminate\Container\Container
[2] configure error handling, configure logging, detect the application environment
[3] resolving type-hinted dependencies, storing/caching stuffs to buffer, etc
[4] Unless retained like stored in class' static property
[5] Illuminate\Routing\UrlGenerator#setRequest
[6] Illuminate\Routing\UrlGenerator, either calling URL::route('route.name'), url('your-url'), app('url')->route('route.name'), or route('route.name'), they refer to the same class
This should should be,
route('index', ['lang' => 'fr']);
And change route helper with
/**
* Generate a URL to a named route.
*
* #param string $name
* #param array $parameters
* #param bool $absolute
* #param \Illuminate\Routing\Route $route
* #return string
*/
function route($name, $parameters = [], $absolute = true, $route = null)
{
if (!isset($parameters['lang'])) $parameters['lang'] = App::getLocale();
return app('url')->route($name, $parameters, $absolute, $route);
}
Refer link for more details.

Using custom ParamConverter for POST Request in Symfony 2

I'm using Symfony 2.6 and the FOS Rest Bundle.
Param converters for PATCH , DELETE and GET requests work nicely and reduce the code in the controller actions. However for POST requests I have a problem. The default \Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter gets called every time. This results in an exception:
Unable to guess how to get a Doctrine instance from the request information.
I checked the \Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener and saw that it's always including the Doctrine param converter in the onKernelController method. From the documentation it seems that the doctrine param converter is automatically applied for all type hinted controller actions unless you set it to off:
sensio_framework_extra:
request:
converters: true
auto_convert: false
I found a kind of hacky way to prevent this. The array of param converters to be applied will be indexed by the name of the type hinted argument in the controller method (which symfony gets by reflection). If I just name my param converter the same as this type hint then the default doctrine param converter will not be added to the list of param converters. For example:
...
* #ParamConverter(
* "MyEntity",
* class="Foo\Bar\MyEntity",
* converter="my_custom_converter"
* )
*
* #param MyEntity $myEntity
* #return MyEntity
*/
public function postMyEntityAction(MyEntity $myEntity)
{
I sort of wrote this question as I was digging deeper into the code and I'm not even really sure what my question is anymore. I guess it's "Is it logical to apply multiple param converters?" or would also like to know if it's possible to turn off param converters for certain actions. Maybe my logic is completely wrong here and this isn't what param converters were intended for.
I'd appreciate any advice.
Alright, I realized where I was going wrong. It was a simple case of not returning true from my custom paramConverter apply method. If it does return true then the doctrine param converter won't be applied.

Where to put new objects generated by middleware?

PSR-7 is going to be standardized soon (I believe). That's got me thinking about middlewares, such as used by Phly, StackPHP, and ConnectJS.
The way ConnectJS works is that it modifies the request object when a middleware needs to add something. For example, cookie-session creates a session property on the req object:
app.use(session({
keys: ['key1', 'key2']
}))
app.use(function (req, res, next) {
var n = req.session.views || 0 // <-- req.session is managed by the session middleware
req.session.views = ++n
res.end(n + ' views')
})
With PSR-7, both our Request and Response objects are (supposed to be) immutable, so how are we supposed to pass along additional data like this? i.e. where would be the best place to store a 'session' object or a 'user' object created by an authentication middleware?
Request and Response objects in PSR-7 are implemented as value objects, hence they are immutable.
Every time you need a different object, you create a new instance from the previous one, like
$newRequest = $oldRequest->withMethod('GET');
and from that point on use the new instance.
In middlewares you would have to pass the new instance to the next() function that calls the next middleware (see here for example).
If you need to store in the request object additional data computed from your current request, in the ServerRequestInterface there are defined the withAttribute and the withAttributes methods that allow you to do exactly that.
A common use case for this is for storing the results of routing, but you can surely use them to store other additional data of the request, like session or user data
Do not store at all. Inject it as parameter into consumer function. For instance:
function doSomething(reqest, response, session, user, foo, bar, ...)
Be explicit.

Using ZF2 Http Request component in a non-framework application, but it's returning no values

I tried to pass parameters from URL, from a post form; $request->getMethod() always returns (GET), and it's not even returning the values.
$request = new \Zend\Http\Request();
echo $request->getMethod(); // GET
print_r($request->getPost('name')); // null
Note: name is the input field's name from the post form.
You should use PhpEnvironment Request class which also derived from Zend\Http\Request like stated by #Xerkus.
The ancestor Http Request class is useless in your case, it used by various classes (for example Zend\Http\Client) internally when dealing with HTTP protocol.
Try this:
$req = new \Zend\Http\PhpEnvironment\Request();
echo $req->getMethod();

Create API using CakePHP

I have a simple CakePHP application that allows a user to create and edit posts. And I'm looking to get the application into PhoneGap at some point in the future.
Therefore I have created an API that spits out JSON for use in AJAX requests, but I get the feeling I'm doing it wrong as I'm not using REST or doing anything any different that sets it apart from other code in the controller.
e.g. (NOTE: I'm missing the part about turning it into JSON for this example)
class ApiController extends AppController {
function index() {
$posts= $this->Post->find('all');
$this->set(compact('posts'));
}
}
To create a url like: domain.com/api/posts/all (would create custom route to achieve this) which I can then call using AJAX to use in my mobile app.
Now my question is what differently would doing it using REST be? I'm very much a newbie to building applications and my strengths are in front-end rather than back-end development so any pointers, help with this would be much appreciated.
Turning on REST in CakePHP basically routes proper HTTP methods to actions. So, a GET request would be routed to an index or view action, a DELETE request routed to the delete action, and so forth.
This creates a very easy endpoint for people using your API. Then when calling this endpoint, depending on the HTTP method Cake will route it to the proper action (forgive any HTTP request syntax errors):
// single endpoint
http://example.com/api/posts
A GET request that routes to /posts/index.json
GET /api/posts.json HTTP/1.1
Host: example.com
A POST request that routes to /posts/edit/1.json
POST /api/posts/1.json HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 24
data[Post][name]=updated
Reading this will answer most of your questions: http://book.cakephp.org/2.0/en/development/rest.html
If your concern is to be true to the Rest principals.
Then, there are usually 4 points to keep in mind:
Base URI for the web service
The Internet media type of the data supported by the web service.
This is often JSON, XML or YAML but can be any other valid Internet
media type.
The set of operations supported by the web service using HTTP methods
(e.g., GET, PUT, POST, or DELETE).
The API must be hypertext driven
See, http://en.wikipedia.org/wiki/Representational_state_transfer for more details.
Now, with that said, I would suggest changing your above code to be some what close to the below pseudo codes.
1) The existence of resources is key, think of your post(s) as a collection of resources that can be accessed by a URI.(authentication & authorization are other concerns that you might also want to handle):
api.domain.com/resources/posts => This URI points to a collection of Posts
2) The set of operations that you will want to support using HTTP methods/verbs need to defined, as an example we might want to retrieve just one member of the collection by doing this:
api.domain.com/resources/posts/12
Below are request header & body that could be found in an incoming request for this URI:
Accept: application/json
Content-Type: application/json
Request Url: http://api.domain.com/resources/posts/12
Request Method: GET
Your application should be able to handle that type of request, without the need of stipulating the operation in the URI, bringing us back to point (1),
rather than having a URI written this way:
domain.com/api/posts/all
Your URI should be model this way:
resources/posts/12 as resources/type/item to retrieve one member from the collection,
resources/posts as resources/type to work with the entire collection.
Here's an example of codes:
Common abstract class
Here you could implement some common tasks.
If, you are using a service based implementation
this could also be accomplished by a service.
abstract class ResourcesController extends AppController {
}
class PostResourcesController extends ResourcesController {
/**
* By the time this method is called in your controller/class, you already know
* that the HTTP method is GET.
*
* #param Request\$_GET $request A request instance
* #param int $postId The post ID to retrieve
*
* #return Response A reponse instance
*/
function getPost(Request $Request, $postId = null)
{
/**
* Here you can use the request object to get
* the response content type
* the requesting client accepts. Example JSON or XML.
*/
/**
* using the $postId you can then query your database
* to retrieve a post with that ID or use a sort of
* service.
*/
/**
* Once you implemented a you logic
* you can build a response to return.
*/
}
}
This code is incomplete, but I hope it gives
you an idea of what a real Restful API might look like.
The key it to make sure that
"the application can interact with a resource by knowing two things: the identifier of the resource and the action required".
Hopefully, this helped.

Categories