Controller with dependency injection does not work - php

When I inject the Request class of Simfony it works well for me, but I just created a class called FormRequest that "extends" from Request, I thought this would work, since it is still a Request instance, but it is not, I get an error.
Type error: Argument 1 passed to AppBundle\Http\Controllers\BlogController::validateAction() must be an instance of AppBundle\Http\FormRequest, instance of Symfony\Component\HttpFoundation\Request given, called in /var/www/html/api-erp/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php on line 151
Exception
My class FormRequest.php:
namespace AppBundle\Http;
use Symfony\Component\HttpFoundation\{JsonResponse, Request, Response};
class FormRequest extends Request
{
}
Controller BlogController.php is:
<?php
namespace AppBundle\Http\Controllers;
use AppBundle\Http\FormRequest;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\{Request, JsonResponse};
use Symfony\Component\Routing\Annotation\Route;
class BlogController extends Controller
{
/**
* #Route("/blog", name="blog_index")
*/
public function validateAction(FormRequest $request)
{
return new JsonResponse(['success' => true]);
}
}
Simfony versiĆ³n: 3.4.*

You missing something.
Symfony use the ParamConverter feature to inject the request in your action. If you want to override it you also have to create a custom converter and use the correct priority in the service to avoid the error.
More explanation in symfony documentation

Related

non-existent class or interface: "Symfony\Config\DoctrineConfig"

I'm trying to programmatically add an entity-manager, with reference to this documentation:
https://symfony.com/doc/6.0/doctrine/multiple_entity_managers.html
However, it needs to be created dynamically, so I'm triggering it by a controller. Unfortunately Symfony does not find the DoctrineConfig class ... although specified as in the docs.
namespace App\Controller;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Config\DoctrineConfig;
class AdministrationController extends AbstractController
{
#[Route('/api/create-org', methods:['POST'])]
public function setUpNewOrg(DoctrineConfig $doctrine)
{
$doctrine->dbal()
->connection('test')
->url('postgresql://Test:test1234127.0.0.1:5432/testDB')
->serverVersion('13')
->charset('utf8');
$emNew = $doctrine->orm()->entityManager('test');
$emNew->mapping('test')
->isBundle(false)
->type('annotation')
->dir('%kernel.project_dir%/src/Entity')
->prefix('App\Entity')
->alias('App');
return $this->json([ 'message' => 'configured' ]);
}
}
However, this leads to following error message:
Cannot resolve argument $doctrine of
"App\Controller\AdministrationController::setUpNewOrg()": Cannot
determine controller argument for
"App\Controller\AdministrationController::setUpNewOrg()": the
$doctrine argument is type-hinted with the non-existent class or
interface: "Symfony\Config\DoctrineConfig".
Does this just not work within a Controller?
Symfony version: 6.0.7
PHP version: 8.1.6

Transform Request to custom validation request

Is it possible to transform Illuminate\Http\Request to custom validation request you made with php artisan make:request MyRequest?
I would like validation to take place in a method down the road so that I have:
protected function register(Request $request)
{
...
$this->userRepository->signup($request)
...
}
User repository:
public function signup(MyRequest $request)
{
...
}
Is this possible? I am getting an error now because one class is expected. Only thing that comes to mind is to make an interface, but I'm not sure if that could function.
Error I get
Type error: Argument 1 passed to UserRepository::signup() must be an
instance of App\Http\Requests\MyRequest, instance of
Illuminate\Http\Request given
Well, you can convert any request to any other request, as long as it extends from Illuminate\Http\Request.
There are basically two methods Laravel uses to convert one request to another. The problem here is that it will never get a validation object or trigger the validation automatically, as part of the injection when MyRequest was passed as an argument. It might also miss a message bag and the error handler, but nothing you can fix by initializing the request just like Laravel does when injecting it.
So you still have to trigger all the sequences the FormRequest (if it extends FromRequest rather than Request) normally does when booting the trait, but still, it's entirely possible and with some little extra work, you could convert any request to any other request.
For example; I'm using this setup to call just one route profile/{section}/save for saving my profile settings. Depending on $section's value, I convert the given $Request to any of my custom form requests for that particular $section.
use App\Http\Requests\MyRequest;
use Illuminate\Http\Request;
...
public function someControllerMethod(Request $Request) {
$MyRequest = MyRequest::createFrom($Request);
// .. or
$MyRequest = MyRequest::createFromBase($Request);
}
...
So to get people started with using a FormRequest as an example, it basically comes to this.
Instead of extending all your custom requests from the default Illuminate\Foundation\Http\FormRequest, use a base class which extends from FormRequest and add a custom method to transform and boot the request as if it were passed as an argument.
namespace App\Http\Requests;
use Illuminate\Routing\Redirector;
use Illuminate\Foundation\Http\FormRequest;
class BaseFormRequest extends FormRequest {
public function convertRequest(string $request_class) : BaseFormRequest {
$Request = $request_class::createFrom($this);
$app = app();
$Request
->setContainer($app)
->setRedirector($app->make(Redirector::class));
$Request->prepareForValidation();
$Request->getValidatorInstance();
return $Request;
}
public function authorize() {
return true;
}
public function rules() {
return [];
}
}
Let all your custom FormRequest extend your BaseFormRequest
namespace App\Http\Requests;
class MyRequest extends BaseFormRequest {
...
}
Now anywhere you want to convert a request, use the base class in your controller method and convert it using convertRequest with the custom request class you wish to convert.
public function someControllerMethod(BaseFormRequest $Request) {
$MyRequest = $Request->convertRequest(MyRequest::class);
}
like #dbf answer but with automatic validation
use App\Http\Requests\MyRequest;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
public function someControllerMethod(Request $Request) {
//this make your request class do validation
try{
app(MyRequest::class);
} catch (ValidationException $ex){
throw $ex;
}
//if no error you can continue to convert the request
$MyRequest = MyRequest::createFrom($Request);
// .. or
$MyRequest = MyRequest::createFromBase($Request);
}
Yes, there is no problem with that, you should create:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyRequest extends FormRequest
{
public function rules() {
// here you put rules
}
}
and in your controller:
public function signup(\App\Http\Requests\MyRequest $request)
{
...
}
Be aware you should also adjust authorize method in your request class (to return true or false depending on user access)
EDIT
After update - you should type hint your custom class in controller and in repository - it's up to you - I often use generic Illuminate\Http\Request, so you should do:
in controller:
public function controllerMethod(\App\Http\Requests\MyRequest $request)
in repository:
public function signup(\App\Http\Requests\MyRequest $request)
or
public function signup(\Illuminate\Http\Request $request)
So to sum up you should use Form request classes in controller - this is the place where validation will be made and later you can use either same class or generic \Illuminate\Http\Request - I personally often use in repositories or services just \Illuminate\Http\Request because they usually don't care about other things put into MyRequest class - they just want to get data from request class and that's it.
I didn't find it was possible to do what I wanted even with my custom class extending the Request because naturally one method expects an instance of one class while getting another one.
Maybe it would be possible to extract an interface out and wrap and bind it but that would be in my opinion a quickfix.
My opinion is that concept I had was wrong from the start, and it was more of an architecture problem so I transformed my app to a different approach and manage to avoid such issues in the first place.

Laravel / Lumen - Reflection Exception Class does not exist

I am trying to inject a Manager class into toe Service Container of Lumen. My goal is to have a single instance of LogManager which is available in the whole application via app(LogManager::class).
Everytime i try to access this shortcut i get the following exeption:
[2017-03-23 16:42:51] lumen.ERROR: ReflectionException: Class LogManager does not
exist in /vendor/illuminate/container/Container.php:681
LogManager.php (i placed that class in the same location where my models are (app/LogManager.php))
<?php
namespace App;
use App\LogEntry;
class LogManager
{
...
}
AppServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\LogManager;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(LogManager::class, function ($app) {
return new LogManager();
});
}
}
I uncommented the line $app->register(App\Providers\AppServiceProvider::class); in bootstrap/app.php
I think that i missed something with the correct namespacing or placement of the classes espaccially LogManager. Maybe some one is willing to give me a hint?
If you need some more informations just give me a hint!
Your class and your service provider look fine. However, wherever you're calling app(LogManager::class) also needs to know the fully qualified name of the class.
Either make sure you have use App\LogManager at the top of the file, or change your call to app(\App\LogManager::class).

Laravel Request::all() Should Not Be Called Statically

In Laravel, I'm trying to call $input = Request::all(); on a store() method in my controller, but I'm getting the following error:
Non-static method Illuminate\Http\Request::all() should not be called statically, assuming $this from incompatible context
Any help figuring out the best way to correct this? (I'm following a Laracast)
The error message is due to the call not going through the Request facade.
Change
use Illuminate\Http\Request;
To
use Request;
and it should start working.
In the config/app.php file, you can find a list of the class aliases. There, you will see that the base class Request has been aliased to the Illuminate\Support\Facades\Request class. Because of this, to use the Request facade in a namespaced file, you need to specify to use the base class: use Request;.
Edit
Since this question seems to get some traffic, I wanted to update the answer a little bit since Laravel 5 was officially released.
While the above is still technically correct and will work, the use Illuminate\Http\Request; statement is included in the new Controller template to help push developers in the direction of using dependency injection versus relying on the Facade.
When injecting the Request object into the constructor (or methods, as available in Laravel 5), it is the Illuminate\Http\Request object that should be injected, and not the Request facade.
So, instead of changing the Controller template to work with the Request facade, it is better recommended to work with the given Controller template and move towards using dependency injection (via constructor or methods).
Example via method
<?php namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class UserController extends Controller {
/**
* Store a newly created resource in storage.
*
* #param Illuminate\Http\Request $request
* #return Response
*/
public function store(Request $request) {
$name = $request->input('name');
}
}
Example via constructor
<?php namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class UserController extends Controller {
protected $request;
public function __construct(Request $request) {
$this->request = $request;
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store() {
$name = $this->request->input('name');
}
}
use the request() helper instead. You don't have to worry about use statements and thus this sort of problem wont happen again.
$input = request()->all();
simple
Inject the request object into the controller using Laravel's magic injection and then access the function non-statically. Laravel will automatically inject concrete dependencies into autoloaded classes
class MyController()
{
protected $request;
public function __construct(\Illuminate\Http\Request $request)
{
$this->request = $request;
}
public function myFunc()
{
$input = $this->request->all();
}
}
The facade is another Request class, access it with the full path:
$input = \Request::all();
From laravel 5 you can also access it through the request() function:
$input = request()->all();
I thought it would be useful for future visitors to provide a bit of an explanation on what is happening here.
The Illuminate\Http\Request class
Laravel's Illuminate\Http\Request class has a method named all (in fact the all method is defined in a trait that the Request class uses, called Illuminate\Http\Concerns\InteractsWithInput). The signature of the all method at the time of writing looks like this:
public function all($keys = null)
This method is not defined as static and so when you try to call the method in a static context, i.e. Illuminate\Http\Request::all() you will get the error displayed in OP's question. The all method is an instance method and deals with information that is present in an instance of the Request class, so calling it in this way makes no sense.
Facades
A facade in Laravel provides developers with a convenient way of accessing objects in the IoC container, and calling methods on those objects. A developer can call a method "statically" on a facade like Request::all(), but the actual method call on the real Illuminate\Http\Request object is not static.
A facade works like a proxy - it refers to an object in the IoC container and passes the static method call onto that object (non-statically). For instance, take the Illuminate\Support\Facades\Request facade, this is what it looks like:
class Request extends Facade
{
protected static function getFacadeAccessor()
{
return 'request';
}
}
Under the hood, the base Illuminate\Support\Facades\Facade class uses some PHP magic, namely the __callStatic method to:
Listen for a static method call, in this case all with no parameters
Grab the underlying object from the IoC container using the key returned by getFacadeAccessor, in this case a Illuminate\Http\Request object
Dynamically call the method that it received statically on the object it has retrieved, in this case all is called non-statically on an instance of Illuminate\Http\Request.
This is why, as #patricus pointed out in his answer above, by changing the use/import statement to refer to the facade, the error is no longer there, because as far as PHP is concerned, all has been correctly called on an instance of Illuminate\Http\Request.
Aliasing
Aliasing is another feature that Laravel provides for convenience. It works by effectively creating alias classes that point to facades in the root namespace. If you take a look at your config/app.php file, under the aliases key, you will find a long list of mappings of strings to facade classes. For example:
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
// ...
'Request' => Illuminate\Support\Facades\Request::class,
Laravel creates these alias classes for you, based on your configuration and this allows you to utilise classes available in the root namespace (as referred to by the string keys of the aliases config) as if you're using the facade itself:
use Request:
class YourController extends Controller
{
public function yourMethod()
{
$input = Request::all();
// ...
}
}
A note on dependency injection
While facades and aliasing are still provided in Laravel, it is possible and usually encouraged to go down the dependency injection route. For example, using constructor injection to achieve the same result:
use Illuminate\Http\Request;
class YourController extends Controller
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function yourMethod()
{
$input = $this->request->all();
// ...
}
}
There are a number of benefits to this approach but in my personal opinion the greatest pro for dependency injection is that it makes your code way easier to test. By declaring the dependencies of your classes as constructor or method arguments, it becomes very easy to mock out those dependencies and unit test your class in isolation.
also it happens when you import following library to api.php file.
this happens by some IDE's suggestion to import it for not finding the Route Class.
just remove it and everything going to work fine.
use Illuminate\Routing\Route;
update:
seems if you add this library it wont lead to error
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
public function store(Request $request){
dd($request->all());
}
is same in context saying
use Request;
public function store(){
dd(Request::all());
}
I was facing this problem even with use Illuminate\Http\Request; line at the top of my controller. Kept pulling my hair till I realized that I was doing $request::ip() instead of $request->ip(). Can happen to you if you didn't sleep all night and are looking at the code at 6am with half-opened eyes.
Hope this helps someone down the road.
i make it work with a scope definition
public function pagar(\Illuminate\Http\Request $request)
{
//

Symfony2 dependecy injection doesn't work with controller

According to Symfony2 Cookbook I'm trying to secure controller via dependecy injection, but I'm getting error Catchable Fatal Error: Argument 1 passed to Acme\ExampleBundle\Controller\DefaultController::__construct() must implement interface Symfony\Component\Security\Core\SecurityContextInterface, none given, called in /var/www/example/app/cache/dev/classes.php on line 4706 and defined in /var/www/example/src/Acme/ExampleBundle/Controller/DefaultController.php line 13
Here is my services.yml
parameters:
acme_example.default.class: Acme\ExampleBundle\Controller\DefaultController
services:
acme_example.default:
class: %acme_example.default.class%
arguments: [#security.context]
and controller:
namespace Acme\ExampleBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class DefaultController extends Controller {
public function __construct(SecurityContextInterface $securityContext)
{
if(false === $securityContext->isGranted('IS_AUTHENTICATED_FULLY'))
{
throw new AccessDeniedException();
}
}
public function indexAction()
{
return new Response('OK');
}
}
If you configure your controllers as services you need to use a slightly different syntax when referencing them in your routes. Instead of AcmeExampleBundle:Default:index you should use acme_example.default:indexAction.
Make sure you use Symfony\Component\Security\Core\SecurityContextInterface; in your controller. Without it, the SecurityContextInterface type hint in the constructor won't resolve.
Also, make sure your controller is actually being called as a service. The error you posted is complaining that nothing was sent to the constructor, which sounds to me like you're using your controller the 'default' way. See this cookbook page on how to setup a controller as a service.
The class Symfony\Bundle\FrameworkBundle\Controller\Controller extends ContainerAware base class. This class ha whole the container accessible via $container local property, so you should not inject any services to a controller service, because you can access SecurityContext via $this->container->get('security.context').

Categories