So I am giving a sample of my controller and web.php file.
controller
{
public function add()
{
//Method called via web.php
}
public function multiply()
{
//how to access this while testing
}
}
web.php
route::get('/add','controller#add');
And now I want to perform unit testing
case1: add $response = $this->get(route('add')); Now it will send a get request when /add is accessed and controller method add will be accessed. And then I can use various assertions to check its functionality.
case2: multiply Here there isn't any route specified, so it wont be able to access controller method.
How can I test my multiply method in such a case
UPDATED ANSWER
So from all the answers and comments, i have understood that in Laravel
Unit Testing - Create a controller instance and then call the method that you want to test.
Feature testing - In this mode of testing, we will be calling the route and testing whether correct method has been hit and it is working as desired.
Thank You!!
You can create the object of controller inside the test file like below.
public function testBasicTest()
{
$object = (new UsersController());
$response = $object->methodName();
$this->assertTrue($response);
}
Related
Lets say I created a custom function called activateAuction should I put the function in my controller or in my model. If I put it in the controller, I will be writing the entire function in the controller. If I put the function in the model, I will call my function from my controller. Which is the right way as I want to write clean code?
Controllers receive a Request and return a Response. That's their only job. Keep them skinny. Skinny controllers and fat models. I don't know what activateAuction does. Maybe it should be in a Repository.
For sure you don't have to place it in the controller, keep it in the model or consider using the repository pattern, it will keep your code clean.
Controller should only be responsible for receiving a request and responding. It would validate and transform the request to pass the params to the function somewhere in your code.
You can either use Repository Pattern or just put in the Model.
You can start on writing your code in Controller, but when you find it verbose or code is getting
repetitive. You can refactor your code and make them a function, then call the function in your Controller.
Example:
class UserController {
...
public function eatCake(Request $request, Cake $cake) {
// Validate Request
$data = $this->validate($request, [
'portion' => 'required|numeric',
]);
// pass params
Auth()->user()->eat($cake, $data['portion']);
// Respond to user
return response('OK');
}
}
class User extends Authenticatable {
...
public function eat(Cake $cake, $portion) : Consume
{
// Your logic
return $this->consumes()->create([
'cake_id' => $cake_id,
'portion' => $portion
]);
}
}
With Slim I group my controllers and generally have an abstract BaseController I extend for each group. I use class based routing:
/* SLIM 2.0 */
// Users API - extends BaseApiController
$app->post('/users/insert/' , 'Controller\Api\UserApiController:insert');
.
.
// Campaigns - extends BaseAdminController
$app->get('/campaigns/', 'Controller\CampaignController:index')->name('campaigns');
and needed to password protect some routes, at other times I needed to have a slightly different configuration. BaseApiController, BaseAdminController... etc. There were times I needed to know which route I was in so I could execute a certain behavior for just that route. In those cases I would have a helper function like so:
/* SLIM 2.0 */
// returns the current route's name
function getRouteName()
{
return Slim\Slim::getInstance()->router()->getCurrentRoute()->getName();
}
This would give me the route name that is currently being used. So I could do something like...
namespace Controller;
abstract class BaseController
{
public function __construct()
{
/* SLIM 2.0 */
// Do not force to login page if in the following routes
if(!in_array(getRouteName(), ['login', 'register', 'sign-out']))
{
header('Location: ' . urlFor('login'));
}
}
}
I cannot find a way to access the route name being executed. I found this link
Slim 3 get current route in middleware
but I get NULL when I try
$request->getAttribute('routeInfo');
I have also tried the suggested:
'determineRouteBeforeAppMiddleware' => true
I've inspected every Slim3 object for properties and methods, I can't seem to find the equivalent for Slim3, or get access to the named route. It doesn't appear that Slim3 even keeps track of what route it executed, it just... executes it.
These are the following methods the router class has and where I suspect this value would be:
//get_class_methods($container->get('router'));
setBasePath
map
dispatch
setDispatcher
getRoutes
getNamedRoute
pushGroup
popGroup
lookupRoute
relativePathFor
pathFor
urlFor
I was hoping someone has done something similar. Sure, there are other hacky ways I could do this ( some I'm already contemplating now ) but I'd prefer using Slim to give me this data. Any Ideas?
NOTE: I'm aware you can do this with middleware, however I'm looking for a solution that will not require middleware. Something that I can use inside the class thats being instantiated by the triggered route. It was possible with Slim2, was hoping that Slim3 had a similar feature.
It's available via the request object, like this:
$request->getAttribute('route')->getName();
Some more details available here
The methods in your controller will all accept request and response as parameters - slim will pass them through for you, so for example in your insert() method:
use \Psr\Http\Message\ServerRequestInterface as request;
class UserApiController {
public function insert( request $request ) {
// handle request here, or pass it on to a getRouteName() method
}
}
After playing around I found a way to do it. It may not be the most efficient way but it works, and although it uses Middleware to accomplish this I think there are other applications for sharing data in the Middleware with controller classes.
First you create a middleware but you use a "Class:Method" string just like you would in a route. Name it whatever you like.
//Middleware to get route name
$app->add('\Middleware\RouteMiddleware:getName');
Then your middleware:
// RouteMiddleware.php
namespace Middleware;
class RouteMiddleware
{
protected $c; // container
public function __construct($c)
{
$this->c = $c; // store the instance as a property
}
public function getName($request, $response, $next)
{
// create a new property in the container to hold the route name
// for later use in ANY controller constructor being
// instantiated by the router
$this->c['currentRoute'] = $request->getAttribute('route')->getName();
return $next($request, $response);
}
}
Then in your routes you create a route with a route name, in this case I'll use "homePage" as the name
// routes.php
$app->get('/home/', 'Controller\HomeController:index')->setName('homePage');
And in your class controller
// HomeController.php
namespace Controller;
class HomeController
{
public function __construct($c)
{
$c->get('currentRoute'); // will give you "homePage"
}
}
This would allow you to do much more then just get a route name, you can also pass values from the middleware to your class constructors.
If anyone else has a better solution please share!
$app->getCurrentRoute()->getName();
$request->getAttribute('route')->getName();
What I wanna do is to know, inside a view, if I'm in a specific controller or not. From what I know, I've got two choices and I don't have the answer to either of them :-D
inject a view variable using the share method in my AppServiceProvider, which involves getting the current controller name(or at least the action name so that I can switch it) inside the service provider.
inject a variable to all the views returned in the controller. For example does controllers have a boot method? Or can I override the view() method in the following code snippet?
public function someAction(Request $request)
{
return view('someview', ['myvar' => $myvalue]);
}
well of course there's the easy (yet not easy :|) solution: add the variable in all methods of the controller. I don't like this one.
Thanks
You could use the controller's construct function.
Add this to the top of your controller:
public function __construct()
{
view()->share('key', 'value');
}
I have a registration form (using Laravel 5 here), which upon submit calls BusinessController#postRegister
public function postRegister(Requests\RegisterBusinessRequest $request)
{
#1. get input, fill new class and save to database
#2. call PayPayController#postBusinessProvider
}
What I'm trying to do is call my PayPalController function to process payment, I've tried return Redirect::action('PayPalController#postBusinessProvider'); but that doesn't seem to work.
Do I have to simply have to create a route to call a function from another Controller or is there another way without creating a route? Or should I just put my PayPal code within the postRegister function, I figured cleaner to seperate?
In MVC model (and in Laravel) a controller will not call another controller in one request. To make your controller business simpler, you can use one of these two options:
Option 1
Separate a controller logic into small parts, each part is solve by a private or protected method of the same controller class. So that your controller will be something like this:
public function postRegister(Requests\RegisterBusinessRequest $request)
{
$this->_validateInput();
$this->_processInput();
$this->_doWithPaypal();
}
Option 2
Create a repository class and to all complicated logic here. In your controller method, get the repository instance and pass your input data as the argument for this instance methods. You can see and example at zizaco/confide package. Your controller will something like this:
public function postRegister(Requests\RegisterBusinessRequest $request)
{
$repo = App::make('PaypalRepositoryClass');
// process your input
// ...
// ...
$repo->paypalBusiness($data);
}
In Slim is it possible to get the current route within middleware?
class Auth extends \Slim\Middleware{
public function call(){
$currentRoute = $this->app->getRoute(); // Something like this?
}
}
I know you can call $app->router()->getCurrentRoute() after the slim.before.dispatch hook is called, but when you call this from middleware it returns a non-object. Any help would be greatly appreciated.
Yes and no. If you look at the source code for Slim, you will see that registered Middlewares are called in LIFO order when the Slim::run method is called, and then Slim runs it's own "call" method where the processing of the request begins. It is in this method that Slim parses and processes the route. In which case, you cannot access $app->router()->getCurrentRoute() in the Middleware::call method because it won't have been parsed and defined yet.
The only way to do this is to register a listener on slim.before.dispatch inside your Middleware, and implement whatever you want to do in that method.
From the name of your class I assume you are trying to create a basic authentication module? I've done something similar to this before, and it went something like this:
class AuthMiddleware extends \Slim\Middleware
{
public function call()
{
$this->app->hook('slim.before.dispatch', array($this, 'onBeforeDispatch'));
$this->next->call();
}
public function onBeforeDispatch()
{
$route = $this->app->router()->getCurrentRoute();
//Here I check if the route is "protected" some how, and if it is, check the
//user has permission, if not, throw either 404 or redirect.
if (is_route_protected() && !user_has_permission())
{
$this->app->redirect('/login?return=' . urlencode(filter_input(INPUT_SERVER, 'REQUEST_URI')));
}
}
}
In this example, the onBeforeDispatch method will be run before of the route handlers are invoked. If you look at the source code, you can see the events are fired inside a try/catch block that is listening for the exceptions thrown by $app->redirect() and $app->pass(), etc. This means we can implement our check/redirect logic here just as if this was a route handler function.
Above is_route_protected and user_has_permission are just pseudo-code to illustrate how my auth middleware worked. I structured the class so that you could specify a list of routes or regex for routes in the Middleware constructor that were protected, as well as passing a service object that implemented the user permission checking, etc. Hope this helps.
There is an alternative method of doing this, as I've been in the same situation. What I wanted to avoid was matching anything by route and wanted to use route names instead, so you could try the following:
public function call() {
$routeIWantToCheckAgainst = $this->slimApp->router()->urlFor('my.route.name');
$requestRoute = $this->slimApp->request()->getPathInfo();
if ($routeIWantToCheckAgainst !== $requestRoute) {
// Do stuff you need to in here
}
$this->next->call();
}
You could even have an array of routes you DON'T want the middleware to run on and then just check if it's in_array() etc and if not, do what you need to.
You should use app->request()->getPathInfo() instead of app->getRoute().
class Auth extends \Slim\Middleware{
public function call(){
$currentRoute = $this->app->request()->getPathInfo();
}
}