I have a paginator class which should get the limit out of the config.xml and the current page from the controller.
How do I define the limit in config.xml and how can I access it outside the controller?
Regards
Define your paginator class as DIC service and add service_container as a service e.g
//paginator class
//namespace definitions
use Symfony\Component\DependencyInjection\ContainerInterface;
class Paginator{
/**
* #var Symfony\Component\DependencyInjection\ContainerInterface
*/
private $container;
public function __construct(ContainerInterface $container){
$this->container = $container;
}
public function yourPaginationMethod(){
$limit = $this->container->getParameter("limit.conf.parameter");
//rest of the method
}
}
And then in you services.yml of your bundle.
#services.yml
paginator_service:
class: FQCN\Of\Your\PaginatorClass
arguments: [#service_container]
And in your controller you can get access of the Paginator in following way.
//in controller method
$paginator = $this->get('paginator_service');
For more info about it you can check Service container section of Symfony Documentation.
Related
I have a repository class called EmailRepository
class EmailRepository extends EntityRepository implements ContainerAwareInterface { ... }
I need to get a parameter injected into this repository class but I dont know how...
This is what I currently have inside of the repository, which is being called from my controller:
Controller:
$em->getRepository(Email::class)->getEmailApi();
Repository
class EmailRepository extends EntityRepository implements ContainerAwareInterface {
protected $container;
public function setContainer(ContainerInterface $container = null) {
$this->container = $container;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
echo $this->container->getParameter('email_api');
}
}
I always get this error:
Call to a member function getParameter() on null
The parameter is not null, it does have a value. I know it's telling me that $this->container is null. How do I fix this?
If I run this inside of my controller, it works fine and returns Google
echo $this->getParameter('email_api');
Inject container not a good idea. Try this
services.yaml
App\Repository\EmailRepository:
arguments:
$emailApi: '%env(EMAIL_API)%'
Repository
class EmailRepository
{
protected $emailApi;
public function __construct(string $emailApi)
{
$this->emailApi = $emailApi;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
return $this->emailApi;
}
}
Or via setter injection
services.yaml
App\Repository\EmailRepository:
calls:
- method: setEmailApi
arguments:
$emailApi: '%env(EMAIL_API)%'
Repository
class EmailRepository extends EntityRepository implements ContainerAwareInterface
{
protected $emailApi;
public function setEmailApi(string $emailApi)
{
$this->emailApi = $emailApi;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
return $this->emailApi;
}
}
Your original code is not going to work because there is nothing calling EmailRepository::setContainer. Furthermore, using ContainerAware and injecting the full container is discouraged.
Fortunately, the Doctrine bundle has a new base repository class that the entity manager can use to pull the repository from container and allow you to inject additional dependencies as needed. Something like:
namespace App\Repository;
use App\Entity\Email;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class EmailRepository extends ServiceEntityRepository // Different class to extend from
{
private $emailApi;
public function __construct(RegistryInterface $registry, ParameterBagInterface $parameterBag)
{
parent::__construct($registry, Email::class);
$this->emailApi = $parameterBag->get('email_api');
}
So in this case we inject all the parameters and then store the ones we need.
Even injecting the parameter bag is a bit frowned upon. Better to inject individual parameters though this takes just a bit more configuration as we need to use services.yaml to explicitly inject the needed parameters:
public function __construct(RegistryInterface $registry, string $emailApi)
{
parent::__construct($registry, Email::class);
$this->emailApi = $emailApi;
}
#services.yaml
App\Repository\EmailRepository:
$emailApi: 'email_api_value'
I have a problem with my application after I moved it to docker container. I newbie with Symfony and I cannot understand where the problem is.
After I want to call my UserController index action my browser throws an error:
Controller "App\Controller\UserController" has required constructor arguments and does not exist in the container. Did you forget to define such a service?
Too few arguments to function App\Controller\UserController::__construct(), 0 passed in /projekty/taskit/vendor/symfony/http-kernel/Controller/ControllerResolver.php on line 133 and exactly 1 expected
My services.yaml file:
parameters:
locale: 'en'
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
public: false # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
App\Entity\UserRepositoryInterface: '#App\DTO\UserAssembler'
UserController.php file
<?php
namespace App\Controller;
use App\Service\UserService;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/**
* Class UserController
* #package App\Controller
*/
class UserController extends AbstractController
{
/**
* #var UserService
*/
private $userService;
/**
* #param UserService $userService
*/
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
/**
* #Route("/users/", name="users_index")
*/
public function index()
{
$users = $this->userService->findAll();
return $this->render('user/index.html.twig', array('users' => $users));
}
}
And my UserService.php file code
<?php
namespace App\Service;
use App\Entity\UserRepositoryInterface;
use Doctrine\ORM\EntityNotFoundException;
/**
* Class UserService
* #package App\Service
*/
class UserService
{
/**
* #var UserRepositoryInterface
*/
private $userRepository;
/**
* #param UserRepositoryInterface $userRepository
*/
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
/**
* #return array
* #throws EntityNotFoundException
*/
public function findAll() : array
{
$users = $this->userRepository->findAll();
if (is_null($users)) {
throw new EntityNotFoundException('Table users is empty');
}
return $users;
}
}
It seems to me you are doing a lot of unnecessary things, which are already part of the framework. Like your service, what does it do? You can call your repositories directly from your controller. And in your controller, you are declaring variables and constructing which is also unnecessary. Just use dependency injection in your function in the controller. This little bit of code should work and replace both your controller and your service (which again, you can get rid of).
<?php
namespace App\Controller;
use App\Repository\UserRepository;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
/**
* Class UserController
* #package App\Controller
*/
class UserController extends AbstractController
{
/**
* #Route("/users/", name="users_index")
*/
public function index(UserRepository $userRepository)
{
$users = $this->userRepository->findAll();
return $this->render('user/index.html.twig', array('users' => $users));
}
}
I am able to use the new autowire feature of Symfony2 to inject a service into a service. However, I cannot inject a service into a controller. What am I not doing/doing wrong?
This is my services.yml file:
services:
home_controller:
class: AppBundle\Controller\HomeController
autowire: true
This is my ServiceConsumerDemo:
namespace AppBundle\Services;
class ServiceConsumerDemo {
private $serviceDemo;
public function __construct(ServiceDemo $serviceDemo) {
$this->serviceDemo = $serviceDemo;
}
public function getMessage(){
return $this->serviceDemo->helloWorld();
}
}
This is ServiceDemo:
namespace AppBundle\Services;
class ServiceDemo
{
public function helloWorld(){
return "hello, world!";
}
}
This is HomeController (which works):
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class HomeController extends Controller
{
/**
* #Route("/")
*
*/
public function indexAction(Request $request)
{
$message[0] = $this->get('service_consumer_demo')->getMessage();
return new \Symfony\Component\HttpFoundation\JsonResponse($message);
}
}
This is HomeController which does not work
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Services\ServiceConsumerDemo;
class HomeController extends Controller
{
private $serviceConsumerDemo;
public function __construct(ServiceConsumerDemo $serviceConsumerDemo) {
$this->serviceConsumerDemo = $serviceConsumerDemo;
}
/**
* #Route("/", name="homepage")
*
*/
public function indexAction(Request $request)
{
$message[0] = $this->serviceConsumerDemo->getMessage();
return new \Symfony\Component\HttpFoundation\JsonResponse($message);
}
}
This is the error thrown:
Catchable Fatal Error: Argument 1 passed to
AppBundle\Controller\HomeController::__construct() must be an instance
of AppBundle\Services\ServiceConsumerDemo, none given,
How do I access the Services in the Controller? I understand I need to declare the controller as a service. So I'm mentioning the controller in the services.yml file. Is there anything else that I need to do?
By default, controllers are not considered as services, so you can't use autowiring with them.
However, it's a simple enough fix - add a #Route annotation on the class (i.e. not on an action method in the class) with a service definition.
From the documentation:
The #Route annotation on a controller class can also be used to assign
the controller class to a service so that the controller resolver will
instantiate the controller by fetching it from the DI container
instead of calling new PostController() itself.
For example:
/**
* #Route(service="app_controller")
*/
class AppController extends Controller
{
private $service;
public function __construct(SomeService $service)
{
$this->service = $service;
}
/** #Route("/") */
public function someAction()
{
$this->service->doSomething();
}
}
and in your config:
app_controller:
class: AppBundle\Controller\AppController
autowire: true
Symfony 3.3+ May 2017 UPDATE!
Symfony 3.3 allows native way to approach this.
# app/config/services.yml
services:
_defaults:
autowire: true # all services in this config are now autowired
App\: # no more manual registration of similar groups of services
resource: ../{Controller}
This means App\Controller\SomeController is autowired by default and to be found in app/Controller/SomeController.php file.
You can read:
more about _defaults here
and more about PSR-4 service registration
Thanks to autowiring in Symfony 2.8+ I could create bundle, that adds autowiring even for controllers: Symplify/ControllerAutowire
You can read more about this in the article.
Usage is as simple as:
class YourController
{
public function __construct(SomeDependency $someDependency)
{
// ...
}
}
Nothing more is required.
You do not pass service to your controller in services.yml. Change it to:
services:
service_demo:
class: AppBundle\Services\ServiceDemo
service_consumer_demo:
class: AppBundle\Services\ServiceConsumerDemo
autowire: true
home_controller:
class: AppBundle\Controller\HomeController
autowire: true
arguments:
service: "#service_consumer_demo"
I have been doing a read up on ZF2 Service Locator component and could say I understand how its being used. I have, however, a question which I think its silly but it wouldn't hurt to ask.
I want to have a namespace inside my Module called Component where I can put generic code in like say FunctionsComponent.php, MailerComponent.php or ExcelComponent.php. This would allow me to do some stuff inside my controllers.
What I would like to tryout is to have an ability to have controllers define the components they are interested to use (see below):
class SalesController extends AbstractController
{
protected $components = ['Excel'];
//In some action
public function exportAction()
{
$data = ['data to be exported'];
/**
$data : data to be exported
boolean : Whether to force download or save the file in a dedicated location
*/
$this->Excel->export($data, true);
}
}
The idea is to create a ComponentCollection that perhaps implements the FactoryInterface or ServiceLocatorInterface and then let it check each controller when the MvcEvent has been triggered inside my Module class and have the ComponentCollection inject all the controller component and make them accessible without using the service locator as shown below:
$excel = $sm->get('Application\Component\Excel');
I am well aware that this may seem like a daunting ask but I feel like the best way to learn a framework among other things is to play around with it and try to do the unimaginable.
You should create a BaseController somewhere and then extend all your Controllers from BaseController. Then you can inject your dependencies in your BaseController and use anywhere in kids. For example, I am doing this in my Controller to set head title:
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class BaseController extends AbstractActionController
{
/**
* Sets the head title for every page
*
* #param string $title
*/
public function setHeadTitle($title)
{
$viewHelperManager = $this->getServiceLocator()->get('ViewHelperManager');
// Getting the headTitle helper from the view helper manager
$headTitleHelper = $viewHelperManager->get('headTitle');
// Setting a separator string for segments
$headTitleHelper->setSeparator(' - ');
// Setting the action, controller, module and site name as title segments
$siteName = 'Ribbon Cutters';
$translator = $this->getServiceLocator()->get('translator');
$title = $translator->translate($title);
$headTitleHelper->append(ucfirst($title));
$headTitleHelper->append($siteName);
}
}
Instead of defining methods, you can define properties.
public $headTitleHelper
and assign it in constructor of BaseController
$this->headTitleHelper = $this->getServiceLocator()->get('ViewHelperManager')->get('headTitle');
Now you can use $this->headTitleHelper in child controllers.
And then
<?php
namespace Application\Controller;
use Zend\View\Model\ViewModel;
use Application\Controller\BaseController;
class IndexController extends BaseController
{
/**
* Property for setting entity manager of doctrine
*/
protected $em;
/**
* landing page
*
* #return ViewModel
*/
public function indexAction()
{
$this->setHeadTitle('Welcome'); // Welcome - Ribbon Cutters
$viewModel = new ViewModel();
return $viewModel;
}
/**
* Sets and gives Doctrine Entity Manager
*
* #return Doctrine Entity Manager
*/
protected function getEntityManager()
{
if (null === $this->em) {
$this->em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
}
return $this->em;
}
}
I think this can help you.
I got a problem like this
I got a service to inject doctrine and use the entity manager to insert a user record into database: UsersService.php
And i got a service to send email: MyEmailService.php
All both services injected in the services.yml(follow this docs http://symfony.com/doc/current/book/service_container.html). All of them work fine.
So now my problem is: I have a class call UserFacade.php(not extends any controller). It has a method "addUser". In this function it will call to UserService.php to insert a record into database, then call the MyEmailService.php to send an email to user's email.
How can i do that in Symphony? I'm the newbie with bundle in Symphony.
Please help
Thanks
First you have to declare your dependencies in the constructor of the UserFacade class. This is one way to allow symfony to inject the dependencies:
class UserFacade
{
/** #var UserService */
private $userService;
/** #var EmailService */
private $emailService;
public function __construct(UserService $userService, MyEmailService $emailService)
{
$this->userService = $userService;
$this->emailService = $emailService;
}
public function addUser(User $user)
{
$this->userService->add($user);
$this->emailService->sendUserMail($user, ...);
}
}
Then you have to declare the dependencies in your service.yml (assuming your using YAML, XML is quite similar):
services:
user_service:
class: UserService
...
email_service:
class: EmailService
...
user_facade:
class: UserFacade
arguments: [#user_service, #email_service]
And then use the facade in your controller:
class UserController
{
public function addUserAction(Request $request)
{
// Do stuff with Request to populate the $user object
$this->get('user_facade')->addUser($user);
}
}
You have to register your UserFacade class as a service, inject into it UserService (and MyEmailService). Then call UserService and MyEmailService from UserFacade service as a property:
$this->userService-><methid>
// and so on