I'm learning symfony 2 dependency injection system. I'm trying to inject Response object in a controller.
ServiceController.php
namespace LD\LearnBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class ServiceController extends Controller
{
public function indexAction(Response $response)
{
}
}
Here is the content of services.yml file (please note that it's included in app/config/config.yml
services:
ld_response:
class: Symfony\Component\HttpFoundation\Response
ld_bla:
class: LD\LearnBundle\ServiceController
arguments: ["#ld_response"]
When I try to access ServiceController I get
Class LD\LearnBundle\Controller\Response does not exist
500 Internal Server Error - ReflectionException
What I'm doing wrong?
There are 2 things wrong here:
1: "Class LD\LearnBundle\Controller\Response does not exist"
The class doesn't exist. You used Response without importing the namespace, so the error message is quite explicit here.
2: You shouldn't inject the response. It doesn't make any sense at all. The response is not a service, it's a value that should be passed down through method parameters.
Here's the fix:
namespace LD\LearnBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response; // was missing
class ServiceController extends Controller
{
public function indexAction()
{
return new Response(); // The Controller is responsible of creating the Response.
}
}
Generally, Class <current-namespace>\class does not exist errors hint to a missing use statement.
May I add that:
you shouldn't declare your services in the app/config/config.yml file (create a specific services.yml file. Even better: create it in a bundle)
you shouldn't inject the Response object: it is the responsibility of the controller to create it
Related
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
In my symfony project I try to configure an email controller without success.
services.yml
emailController:
class: AppBundle\Controller\emailController
public: true
arguments:
$mailer: '#mailer'
emailController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use FOS\UserBundle\FOSUserEvents;
use Doctrine\ORM\EntityManagerInterface;
class emailController extends Controller
{
protected $mailer;
function __construct(\Swift_Mailer $mailer) {
$this->mailer = $mailer;
}
public function sendMail($email){
$message = (new \Swift_Message())
->setSubject('send mail')
->setFrom('xx#yy.com')
->setTo($email)
->setBody('TEST')
->setContentType("text/html");
$this->mailer->send($message);
return 1;
}
}
Symfony return this message:
Catchable Fatal Error: Argument 1 passed to
AppBundle\Controller\emailController::__construct() must be an
instance of Swift_Mailer, none given,
I try some configuration and option but without success
I think the problem is, that you are configuring your controller as a service, but your router probably does not refer to the configured service, only to the class name.
You can use annotations:
#Route(service="emailController")
or the typical yaml format to refer to your controller as a service:
email:
path: /email
defaults: { _controller: emailController:indexAction }
Note that both refer to the service id specified in your definition above, not the actual class name. You can read more about the concept of controllers as services in the documentation: https://symfony.com/doc/current/controller/service.html
edit: As a sidenote since you seem to use a new Symfony version you might want to check out injecting services directly into actions using the resolve_controller_arguments tag: https://symfony.com/doc/current/controller.html#fetching-services-as-controller-arguments
You define a controller as a service which is not how it's intended to be but whatever; both controller and services are normal PHP classes.
Anyway, the aforementioned error message says it all, your service definition needs to supply the correct arguments (none given) e.g. like that:
arguments: ['#mailer']
Please try this.
I am all about the best practice of symfony 2 and I would like to integrate a php library into the project. Library is a non bundle, a simple php class with some methods.
My question just follows the following, which DOES NOT have an accepted answer. Anyway from what I read here I decided to autoload the class, but have no idea where should I locate the php file.
Maybe src/MyBundle/DependencyInjection/? I really doubt it since library has no dependency of other services I have.
Should I create a directory like src/MyBundle/Services/ or src/MyBundle/Libraries/?
What is the best practice here?
as mentioned by b.enoit.be create a service from the class.
MyBundle/Service/MyServiceClass.php
<?php
namespace MyBundle\Service;
class MyService
{
...
}
app/config/services.yml
services:
app.my_service:
class: MyBundle\Service\MyService
use it e.g. in a controller
MyBundle/Controller/DefaultController.php
<?php
namespace MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function indexAction(Request $request)
{
...
// get the service from the container
$yourService = $this->get('app.my_service');
...
}
}
?>
I have the following controller:
namespace Acme\CompanyBundle\Controller;
use Symfony\Component\DependencyInjection\Container;
/**
* Company controller.
*
*/
class CompanyController extends Controller
{
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function getData()
{
$userObj = $this->container->get('security.context')->getToken()->getUser();
}
}
In my services.yml file, I have injected Container class:
parameters:
acme.controller.company.class: Acme\ContainerBundle\Controller\CompanyController
services:
acme.controller.company:
class: %acme.controller.company.class%
arguments: [#service_container]
When loading this controller, I get following error:
Catchable Fatal Error: Argument 1 passed to
Acme\CompanyBundle\Controller\CompanyController::__construct() must be
an instance of Symfony\Component\DependencyInjection\Container, none
given, called in C:\wamp\www\symfony\app\cache\dev\classes.php on line
2785 and defined in
C:\wamp\www\symfony\src\Acme\CompanyBundle\Controller\CompanyController.php
line ...
As you could see, this is a simple injection of Container object into a controller but throws nice errors. What is the problem here?
Similar issue is posted in another SO thread here.
You don't need to inject the container in controllers as long as they extend the base Controller class, which yours do.
Just do:
namespace Acme\CompanyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* Company controller.
*
*/
class CompanyController extends Controller
{
public function getData()
{
$userObj = $this->get('security.context')->getToken()->getUser();
}
}
By default, routes look something like this:
cerad_player_wanabe_list:
pattern: /player-request/list
defaults:
_controller: CeradPlayerWanabeBundle:Player/PlayerList:list
The Symfony\Component\HttpKernel\HttpKernel::handle($request) method pulls the _controller attribute from the request object. If the attribute has two colons in it then it translates the attribute into a class name and creates an instance using the new operator. If the instance implements the ContainerAwareInterface then the container is injected into the controller instance. The controller service you defined is not used. Hence the error about no argument being passed to the constructor.
On the other hand, if _controller has only one colon then the controller is pulled as a service from the container. There is no checking for the ContainerAwareInterface. It's up to you to inject the dependencies via your service definition.
This is all documented in: http://symfony.com/doc/current/cookbook/controller/service.html
So for this particular question, your route should be something like:
cerad_player_wanabe_list:
pattern: /player-request/list
defaults:
_controller: acme.controller.company:action
This does raise the question of why you are trying to define the controller as a service. The default approach already does exactly what you want so you are not gaining anything.
The rationale for defining services as containers is that you can control exactly what dependencies the controller uses. Makes the controller easier to understand and test.
Injecting the complete container pretty much destroys the value of defining the controller as a service.
Never and never inject the container inside something (services, controller or whatever)
Instead try to inject the securityContext or access it through the helper method of symfony controller as suggested above.
The token it's not an object just because probably the route of the controller it's not under a firewall
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').