Missing argument 1 for MyController in RESTful application - php

I am developing app in laravel (REST server), using Basic Auth. Using Postman, all GET requests I have implemented seem to work, but unfortunately POST requests not.
routes.php:
Route::post('my/action', 'MyController#postMyAction');
My Controller:
public function __construct()
{
$this->middleware('auth.basic.once');
}
public function postMyAction($request)
{
// some logic here
}
The problem is, that this way, after setting credentials and some params in Postman, following exception appears:
Missing argument 1 for
App\Http\Controllers\MyController::postMyAction()
Does anybody knows how to put request into post-processing function defined in routes?
Thanks in advance.

Laravel provides dependency injection for controller methods, however you need to typehint exactly what you want so Laravel knows what to inject:
public function postMyAction(\Illuminate\Http\Request $request)
{
// Now $request is available
Now Laravel knows you want an instance of Illuminate\Http\Request and it will give it to you.
Of course you can also stick use Illuminate\Http\Request; at the top of your controller then just typehint Request $request as the argument.

Related

Laravel Jetstream Custom Authentication Login Process Pass Request

I've been reading and following jetstream's documentation and I wanted to create a custom class for the authentication process:
If you prefer to encapsulate your custom authentication process within
a class instead of a closure, you may pass a PHP "callable" array to
the authenticateUsing method:
use App\Actions\AuthenticateLoginAttempt;
use Laravel\Fortify\Fortify;
Fortify::authenticateUsing([new AuthenticateLoginAttempt, '__invoke']);
I have already created the file AuthenticateLoginAttempt in App/Actions folder:
<?php
namespace App\Actions;
class AuthenticateLoginAttempt
{
public function __invoke()
{
}
}
What I wanted is also to pass the Request $request as an argument that will be used later in the __invoke magic method:
Fortify::authenticateUsing([new AuthenticateLoginAttempt, '__invoke', ...Request $request...]);
public function __invoke(Request $request)
{
}
How to achieve this one? It seems to me that their documentation is unclear to me. Thank you.

Laravel - Authorization Works from Controller Method only

I have setup my model policy and it seems to be working when I authorize actions from within controller actions.
// create action
public function create()
{
$this->authorize('create', BusinessProfile::class);
return view('business-profile.create');
}
The policy for create simply returns true or false and switching the Boolean seems to be working as I am authorized based on it.
This conforms that my policies are set up correctly.
However, instead of using the authorize method everywhere in my controller, I tried to set up the middleware in my constructor.
The Laravel documentation shows this example.
Route::post('/post', function () {
// The current user may create posts...
})->middleware('can:create,App\Post');
So, I wrote this in my controller constructor.
public function __construct()
{
$this->middleware('auth');
$this->middleware('can:create,BusinessProfile')->only('create');
}
However, when doing this, the action is always unauthorized.
Bonus Information
I went ahead and wrote garbage code in my policy to raise a syntax error and still, I get an unauthorized response which tells me my policy is not firing at all. It could be that I have not registered my policy correctly but as mentioned above, $this->authorize(...) works as expected.
It seems there you used alias for your model while it requires model name. At documentation states:
some actions like create may not require a model instance. In these
situations, you may pass a class name to the middleware. The class
name will be used to determine which policy to use when authorizing
the action:
You can find more information here: https://laravel.com/docs/5.4/authorization#policy-methods
So in the controller constructor this line:
$this->middleware('can:create,BusinessProfile')->only('creat‌​e');
will become:
$this->middleware('can:create,App\BusinessProfile')->only('c‌​reate');

Access current route name in Slim3 controller's class constructor

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();

Laravel 5 / Lumen Request Header?

So I am not really sure how to go about this I have tried a few things and I will list one below however what I am trying to do is store information sent in a http request in a PHP variable.
Here is a view from Chrome Postman of me sending the request I want ot send. Note "pubapi" is a "header".
I have been messing around with Lumen requests as you can see documented here ( http://lumen.laravel.com/docs/requests ) and have tried using the following below to possibly display them but its not working obviously.
echo Request::all();
I am putting this in my controller and I have ...
use Illuminate\Http\Request;
in my controller.
So how could I say store the header I am sending "pubapi" into a php variable in my controller?
EDIT
Not sure if this will help, however looking at the Laravel frameworks docs I see this http://laravel.com/api/5.0/Illuminate/Http/Request.html#method_header trying this throws the same error in my code. So for example I tried the following and reached the same error.
echo Request::header('pubapi');
You misunderstand the Laravel request object on two levels.
First, the error you are getting is because you were referencing the object instead of the Facade. Facades have a way of forwarding static method calls to non-static methods.
Second, you are sending the value as a header but are trying to access the request parameters. This will never give you what you want.
Here is a simple way to see an example of what you want by creating a test route like so:
Route::match(['get','post'], '/test', function (Illuminate\Http\Request $request) {
dd($request->headers->all());
});
Post to this route and you will see your headers, one of which will be pubapi. Pay attention that the route method definition matches how you are submitting the request (ie GET or POST).
Let's apply this to the controller, ArticleController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
public function index(Request $request)
{
$pubapi = $request->header('pubapi'); // string
$headers = $request->headers->all(); // array
/*
$pubapi === $headers['pubapi']
*/
}
}
Try to change the Illuminate\Http\Request to Request.
- use Illuminate\Http\Request;
+ use Request;
Using
echo app('request')->header('pubapi');
Instead of
echo Request::header('pubapi');
Seemed to work perfect. Could someone provide additional explanation to why this worked and my original method didn't?
Actually you are calling it statically, that's why it is not getting appropriate Request class and throwing error, can do as follows
use Illuminate\Http\Request;
//inside your controller
class YourClass extends Controller{
public function yourFunction(Request $request){
//for getting all the request
dd($request->all());
//for getting header content
dd($request->header('pubapi'));
}
}
You can do it by request()->header('pubapi') also.
I prefer to use request() helper function than Request class its self. Because it can be used without use/import any class in the controller.

Laravel routes with multiple optional parameters

I am building a RESTful api using Laravel. I am confused on how to do the routing.
I have the following api controller
class APIController extends BaseController{
public function sendMsg($authid, $roomid, $msg){
}
public function getMsg($roomid, $timestamp){
}
}
The URL format I want this to be accessible looks like this:
http://example.com/api/{functionName}/{parameter1}/{parameter2}/.../
Here, in the first parameter, I will have the function name which should map to the function in the controller class and following that the parameters the controller needs.
For example To access the sendMsg() function, the url should look like this:
http://example.com/api/sendMsg/sdf879s8/2/hi+there+whats+up
To access the getMsg() function, the url should look like
http://example.com/api/getMsg/2/1395796678
So... how can I write my routes so that it can handle the dynamic number and different parameters need?
I can write one route for each function name like so:
Route::get('/api/sendmsg/{authid}/{msg}', function($authid, $msg){
//call function...
});
and same for the other function. This if fine but is there a way to combine all function to the APIController in one route?
Yes, you can combine all the function to your APIController in one route by using a resourceful controller which is best suited for building an API:
Route::resource('api' ,'APIController');
But, technically, it's not one route at all, instead Laravel generates multiple routes for each function, to check routes, you may run php artisan routes command from your command prompt/terminal.
To, create a resourceful controller you may run the following command from your command line:
php artisan controller:make APIController
This will create a controller with 6 functions (skeleton/structure only) and each function would be mapped to a HTTP verb. It means, depending on the request type (GET/POST etc) the function will be invoked. For example, if a request is made using http://domain.com/api using GET request then the getIndex method will be invoked.
public function getIndex()
{
// ...
}
You should check the documentation for proper understanding in depth. This is known as RESTful api.

Categories