Test route dispatch - laravel

I have an API with a scenario that forwards a request onto another controller.
ie:
$request = Request::create'/new/resource', 'POST');
Route::dispatch($request);
How would I write a test to verify this dispatch happens as I expect? (I know I'm not supposed to mock the 'Request' facade)
ie:
class MyTest extends TestCase {
public function myTestShouldForwardRequestCorrectly()
{
// Once for initial request, and once for fwd request?
// Maybe also test w/ mockery spy which resource the Request is going to?
$m = Route::shouldReceive('dispatch')->times(2);
.... setup test ...
$this->call('POST', '/initial/resource', $parameters);
}
}

You dont have to mock Request - just verify that it is beeing sent. Dispacth will be verified upon controller action. When you call:
$this->call("POST","/path",$params);
You are triggering route collection which act as a lookup table. That collection is storing all predefined routes. So you will have to hook into that collection, to verify your own dispatch. Whole response process ih handled by TTPKernelInterface, and act like a stack - which returns response to user. To test, you will have to do a lot of mocking, because of dependencies. You may take a look at core Laravel tests directory. There you can find how Taylor Otwell tested whole framework without application - by simulating request. I learned a lot from there.

Related

How does a Laravel controller method handle multiple Form Request parameters?

I am getting back to Laravel after several years and trying to understand how an already existing REST API coded in Laravel works. I can't understand how a particular controller method with multiple Form Request parameters works (or if it actually does).
The REST API was coded in Laravel 5.1. I've looked at the official documentation (both 5.1 and the latest) and tried to search the web and SO for related topics (e.g., "laravel controller multiple form requests", "laravel controller multiple type-hint requests", etc.), but I can't seem to find a clear explanation. Maybe I'm looking at it from a wrong angle.
public function store(ProductRequest $productRequest, PromoRequest $promoRequest)
{
// Validate product
$product = new Product($productRequest->all());
// Validate promo
if ($promoRequest->get('promo')) {
$promo = new Promo($promoRequest->get('promo'));
}
...
}
In most documentation, the controller would accept only one Request object. I did actually see some examples that have multiple Form Request parameters, but often they were recommended to use only one Form Request. But best practice aside, how does this code work? When this method is called, how does Laravel know how to split the Request into two separate Form Request classes?
Please feel free to let me know if and how I can explain my question more clearly.
you can write like this:
function example(Request $request) {
$productRequest = new ProductRequest($request->all());
$promoRequest = new PromoRequest($request->all());
}
but on validate you should change the way to this:
$data = $productRequest->validate($productRequest->rules());
another way that i test in laravel 8 is to add both form request object in methd parameters like :
function example(ProductRequest $productRequest,PromoRequest $promoRequest) {
$productRequest->validated();
$promoRequest->validated();
}
The Laravel service container is a powerful tool for managing class
dependencies and performing dependency injection. Dependency injection
is a fancy phrase that essentially means this: class dependencies are
"injected" into the class via the constructor or, in some cases,
"setter" methods.
You can read more about it here: https://laravel.com/docs/5.8/container
Edit: Additional question: Still, I can't understand how the HTTP request (which is only one) can be split into two different Request classes?
The HTTP request isn't split; it is merely sent through both classes.
Ex:
function example(Request $request) {
$productRequest = new ProductRequest($request);
$promoRequest = new PromoRequest($request);
}
Would be the same.

\Request::getSession() returns NULL in Controller construct

I want to get the session id of the user each time a page is loaded so I use \Request::getSession()->getId() in the controller construct but I get this error
Call to a member function getId() on null
If I use same code in any of the route methods, it works.
How can I solve this?
Try use Illuminate\Support\Facades\Auth; Auth::getSession()->getId();
This is the default functionality of Laravel. Session data is no longer available in the constructor. See:
laravel - Can't get session in controller constructor
Taylor's word about it :
It’s very bad to use session or auth in your constructor as no request has happened yet and session and auth are INHERENTLY tied to an HTTP request. You should receive this request in an actual controller method which you can call multiple times with multiple different requests. By forcing your controller to resolve session or auth information in the constructor you are now forcing your entire controller to ignore the actual incoming request which can cause significant problems when testing, etc.
This worked for me:
public function __construct()
{
// No session access from constructor work around
$this->middleware(function ($request, $next) {
\Request::getSession()->getId();
return $next($request);
});
}

Using a single policy method to cover every action on a resource

I have a resource named Post. Every Post is related to a single User, and a User may have several (or zero) Posts.
I'm using Route::resource to map routes for every possible action on the Post resource to a single resource controller, PostController.
Now, I wish to implement a policy for the Post resource. Aiming to keep everything as simple and fool-proof as possible, I'd like to make the policy as follows:
Every user is authorized to make any action that doesn't require an existing Post (e.g. create).
A User is only authorized to access its own Posts for every action that accesses a Post (e.g. edit, update, delete).
What I'm trying to do right now is to create a single policy method called access, which would check that the Post is owned by the User. Then, in my PostController, every method that has a Post object in its parameters (such as edit(Post $post)) would begin with
$this->authorize('access', $post);
However, I don't like that I need to put those lines manually. If I ever forget one of those, I'll have a security hole right there.
On the other hand, I have the authorizeResource method, which makes authorization automatic but requires the policy to have several methods so they are mapped to the each of the controller's methods. Also, I tried to use the Authorize/can middleware, but it didn't work (maybe because I used it on a Route::resource mapping).
The question is: What would be the cleanest and more secure way to achieve the result I described (i.e. authorizing every possible action that accesses a resource with the exact same rule)?
You can use authorizeResource() and override the resourceAbilityMap() method in your controller. The latter method returns a mapping from controller methods to the policy methods that will be called.
https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Auth/Access/AuthorizesRequests.php#L105
E.g.
class MyController extends Controller
{
// ...
/**
* Get the map of resource methods to ability names.
*
* #return array
*/
protected function resourceAbilityMap()
{
return [
'edit' => 'access',
'update' => 'access',
'destroy' => 'access',
];
}
// ...
}

Laravel 5 - Middleware get user ID and send to controllers

Hello guys !
I'm working on an API that has a middleware authenticating the user with a unique ID. After making sure that this user exists, I want to send his database ID to the controller coming next, whichever it is.
Is that a good idea ? Or should I get that ID somehow after the middleware finished ?
How do I do that ?
Thanks !
Is that a good idea ? Or should I get that ID somehow after the middleware finished ?
It depends on what you want to do and how you routes are declared.
The routing is one of the first thing initialized by Laravel. You cannot pass parameter at run time (correct me if I'm wrong).
Plus, the controllers called after all midlewares has done their work.
I cannot garanty it's the more "beautiful" way to do this, but what i'm use to do is using Session::flash() or Session::put() when I want to pass parameters to my controllers at run time.
I use Session::flash() if the parameter has a one request life time, and Session::put() when I want the variable be more 'consistent' across the whole application.
I don't know if I am clear or not, tell me :)
Well, as long as you don't send that ID passing through the HTTP protocol, you should be fine since the user won't be able to tamper with the data.
That said, if you are using Laravel's built-in Auth module, you should just do an Auth::user() call at the other controller and it will give you the authenticated user.
If that isn't an option, you should create a function in the other controller that accepts $id as a parameter. You can call that function from within the first controlling by constructing the second controller throug $secondController = App->make(SecondController) and then $secondController->receiverFunction($id)
If you want the currently-authenticated user available in your application, just add it your base controller:
<?php namespace App\Http\Controllers;
use Illuminate\Contracts\Auth\Guard;
abstract class Controller {
protected $auth;
protected $currentUser;
public function __construct(Guard $auth)
{
$this->auth = $auth;
$this->currentUser = $this->auth->user();
}
}

How to get synchronous HTTP response?

I write a class to handle the request to web. And it has a method which is using WebClient actually to do the main job. When the DownloadStringCompleted method has been done, I want to return the value of the response.
I want to use that like this:
// the pubTimeLine() method returns the value
// of the request to the web using WebClient
textBlock1.DataContext = wp.pubTimeLine(url);
How to make it? Or how to get the synchronous response of HTTP request?
You should never make synchronous network calls, it will freeze up your UI (and therefore your phone) which is a very bad user experience.
Instead do it asynchronously, something like:
wp.pubTimeLine(url, result => textBlock1.DataContext = result);
Where the second parameter is a lambda expression containing the callback that is called when the pubTimeLine method is done executing asynchronously.

Resources