I'm trying to use session variables in my custom service.
I already set add the following lines to services.yaml
MySession:
class: App\Services\SessionTest
arguments: ['#session', '#service_container']
And my SessionTest looks like this
namespace App\Services;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpFoundation\Request;
class SessionTest
{
public $session;
public function __construct()
{
}
public function index()
{
echo "<pre>";
var_dump($this->session);
echo "</pre>";
}
}
And receive this error:
Too few arguments to function App\Services\SessionTest::__construct(), 0 passed in /var/www/app.dev/src/Controller/OrdersController.php on line 33 and exactly 1 expected
You are using constructor injection in your config, but it looks like you're trying to accept property injection in TestSession.
The container will be generated with code like approximately this:
new SessionTest(SessionInterface $session, ContainterInterface $container)
So you either need to change TestSession to accept '#session' and '#service_container' as constructor arguments:
protected $session;
protected $serviceContainer;
public function __construct(SessionInterface $session, ContainterInterface $serviceContainer)
{
$this->sessions = $session;
$this->serviceContainer = $container;
}
Or you need to change your config to something like this:
MySession:
class: App\Services\SessionTest
properties:
session: '#session'
serviceContainer: '#service_container'
You will also need to add
public $serviceContainer;
to TestSession if you want to inject the service container as a property.
Related
I have written one function as getAccess() in controller file Appbundle/Controller/BackendController.php.
I want to access this controller's method in Menu/Menubuilder.php file. How can I do that?
Menu and Appbundle folders are at same level.
For me, a controller can not be called in menuBuilder and it would not be "clean". I suggest you create a manager or service that contains this feature and call your service in your controller and in MenuBuilder.
namespace App\Service;
class MessageGenerator
{
public function getHappyMessage()
{
$messages = [
'You did it! You updated the system! Amazing!',
'That was one of the coolest updates I\'ve seen all day!',
'Great work! Keep going!',
];
$index = array_rand($messages);
return $messages[$index];
}
}
What version of symfony are you on?
You can use Trait
Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.
So, you can create your function getAccess() in trait file and just use it in BackendController.php and Menubuilder.php
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}
class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}
class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
I have created service as follows:
namespace AppBundle\Services;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class UserAccessService {
private $conn;
private $container;
private $tokenStorage;
public function __construct(EntityManagerInterface $entityManager, ContainerInterface $container, TokenStorageInterface $tokenStorage) {
$this->conn = $entityManager;
$this->container = $container;
$this->tokenStorage = $tokenStorage;
}
and added following code in services.yml:
app.service.useraccessservice:
class: AppBundle\Services\UserAccessService
arguments: ['#doctrine.orm.default_entity_manager','#service_container','#security.token_storage']
app.menu_builder:
class: AppBundle\Menu\MenuBuilder
arguments: ["#knp_menu.factory", "#security.authorization_checker", '#security.token_storage', '#translator', '#app.service.useraccessservice','#kernel']
public: true
tags:
- { name: knp_menu.menu_builder, method: createMainMenu, alias: main_menu }
- { name: knp_menu.menu_builder, method: createManagementMenu, alias: management_menu }
- { name: knp_menu.menu_builder, method: createUserMenu, alias: user_menu }
It works as expected.
Since the last 4 hours I'm trying to understand the logic of Symfony 2 services and how they integrate in the application...
Basically I'm trying to set my EntityManager via a service and use it in a controller
I have the following structure
Bundle1/Controller/Bundle1Controller.php
Bundle1/Services/EntityService.php
Bundle2/Controller/Bundle2Controller.php
Bundle3/Controller/Bundle3Controller.php
....
I'm trying to make a REST API with different entry points, that's why I use multiple bundles bundle2,bundle3....
The logic is the following:
A POST is fired to Bundle2/Controller/Bundle2Controller.php
Bundle2Controller.php instances a new() Bundle1Controller.php
Inside Bundle1Controller I want to access a service entity_service in order to get my EntityManager
I have 2 cases in which I manage to land...
In Bundle1/Controller/Bundle1Controller if I try $this->container or $this->get('entity_service') I get a null everytime
If I set the container in Bundle2/Controller/Bundle2Controller and try $this->get('entity_service') I get You have requested a non-existent service "entity_service"
I will place all the code below
Bundle1/Controller/Bundle1Controller
<?php
namespace Bundle1\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use EntityBundle\Entity\TestEntity;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
class Bundle1Controller extends Controller
{
/**
* #param $response
* #return array
*/
public function verifyWebHookRespone($response){
$em = $this->get('entity_service')->getEm();
$array = json_decode($response);
$mapping = $em->getRepository('EntityBundle:TestEntity')
->findBy(["phone" => $array['usernumber']]);
return $mapping;
}
}
Bundle2/Controller/Bundle2Controller.php
<?php
namespace Bundle2\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Bundle1\Controller\Bundle1Controller;
class Bundle2Controller extends Controller
{
public function webhookAction(Request $request)
{
$data = $request->request->get('messages');
$model = new Bundle1Controller();
$responseMessage = $model->verifyWebHookRespone($data);
return new Response($responseMessage, Response::HTTP_CREATED, ['X-My-Header' => 'My Value']);
}
}
Bundle1/Services/EntityService.php
<?php
namespace EntityBundle\Services;
use Doctrine\ORM\EntityManager;
use Symfony\Component\DependencyInjection\Container;
class EntityService
{
protected $em;
private $container;
public function __construct(EntityManager $entityManager, Container $container)
{
$this->em = $entityManager;
$this->container = $container;
}
/**
* #return EntityManager
*/
public function getEm()
{
return $this->em;
}
}
services.yml
services:
entity_service:
class: Bundle1\Services\EntityService
arguments: [ "#doctrine.orm.entity_manager" , "#service_container" ]
Can anyone please help me with something regarding this issue?
How can I register a service and call it from anywhere no matter the bundle or another service?
You should check where your services.yml is located and whether it is imported in the config.yml
You can't just instantiate a controller and expect it to work, you need to set the container.
But you can call EntityManager without needing any other service by using;
$this->get('doctrine.orm.entity_manager');
I can't understand your structure or what you are trying to achieve, but those are the options to go about if you want to keep this structure.
I was using Authentication success handler to populate some values on session on every success login. I wanted some database operation to be done so i pass #doctrine.dbal.default_connection from my config file. Here is my config file where i override success_handler function.
services:
security.authentication.success_handler:
class: XYZ\UserBundle\Handler\AuthenticationSuccessHandler
arguments: ["#security.http_utils", {}, #doctrine.dbal.default_connection]
tags:
- { name: 'monolog.logger', channel: 'security' }
In AuthenticationSuccessHandler.php my code is like this...
namespace Sourcen\UserBundle\Handler;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
use Symfony\Component\Security\Http\HttpUtils;
use Doctrine\DBAL\Connection;
class AuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler {
private $connection;
public function __construct( HttpUtils $httpUtils, array $options, Connection $dbalConnection ) {
$this->connection = $dbalConnection;
parent::__construct( $httpUtils, $options );
}
public function onAuthenticationSuccess( Request $request, TokenInterface $token ) {
$response = parent::onAuthenticationSuccess( $request, $token );
// DB CODE GOES
return $response;
}
}
This is working when i execute some controller URL directly. But when i execute my app home url like "www.xyz.com/web" it throws following error...
Catchable fatal error: Argument 3 passed to XYZ\UserBundle\Handler\AuthenticationSuccessHandler::__construct() must be an instance of Doctrine\DBAL\Connection, none given, called in /opt/lampp/xyz/app/cache/prod/appProdProjectContainer.php on line 1006 and defined in /opt/lampp/xyz/src/Sourcen/UserBundle/Handler/AuthenticationSuccessHandler.php on line 18
Any idea how it can be solved ?
You don't need to extends the DefaultAuthenticationSuccessHandlerclass.
Try to define your service class like:
namespace XYZ\UserBundle\Handler;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Doctrine\DBAL\Connection;
class AuthenticationSuccessHandler {
private $connection;
public function __construct( Connection $dbalConnection ) {
$this->connection = $dbalConnection;
}
public function onAuthenticationSuccess( InteractiveLoginEvent $event ) {
$user = $event->getAuthenticationToken()->getUser();
// DB CODE GOES
return $response;
}
}
and configure your service tagged to the event listener component security.interactive_login
services:
security.authentication.success_handler:
class: XYZ\UserBundle\Handler\AuthenticationSuccessHandler
arguments: [#doctrine.dbal.default_connection]
tags:
- { name: 'kernel.event_listener', event: 'security.interactive_login'. method:'onAuthenticationSuccess' }
PS: Why don't you use the doctrine.orm.entity_managerinstead of doctrine.dbal.default_connection? (In my sf i haven't this service dumped in the php app/console container:debug command)
I am trying to create a service in Symfony2 to automatically pass Doctrine\ORM\EntityManager to __construct to avoid having to pass it each time I instantiate the class, i.e.
// use this
$TestClass= new TestClass;
// instead of this
$entityManager = $this->getDoctrine()->getEntityManager();
$TestClass= new TestClass($entityManager);
I created a class EntityManagerUser, tried to register it as a service and TestClass extends that.
services.yml is included, as another service works, and I've double-checked by adding (then removing) a syntax error.
I read the docs, this, this and this and I've ended up with the code below, which doesn't pass #doctrine.orm.entity_manager. However, the controller_listener service does receive #templating.
I've cleared cache via the console and manually deleted app/cache but I still see this error:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Test\TestBundle\ServiceUser\EntityManagerUser::__construct() must be an instance of Test\TestBundle\ServiceUser\Doctrine\ORM\EntityManager, none given, called in D:\Documents\www\Test\live\src\Test\TestBundle\Controller\MyController.php on line 84 and defined in D:\Documents\www\Test\live\src\Test\TestBundle\ServiceUser\EntityManagerUser.php line 14
services.yml
services:
# this one doesn't throw an error and passes #templating to __construct
test.eventlistener.before_controller_listener:
class: Test\TestBundle\Eventlistener\BeforeControllerListener
arguments: [ #templating ]
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
# the following one doesn't pass #doctrine.orm.entity_manager
test.service_user.entity_manager_user:
class: Test\TestBundle\ServiceUser\EntityManagerUser
arguments: [ #doctrine.orm.entity_manager ]
src/Test/TestBundle/ServiceUser/EntityManagerUser.php
namespace Test\TestBundle\ServiceUser;
use Doctrine\ORM\EntityManager;
class EntityManagerUser{
protected $entityManager;
public function __construct(EntityManager $entityManager){
$this->entityManager = $entityManager;
// N.B. it's not possible to do it this way:
// $this->entityManager = new EntityManager;
}
// also tried public function __construct($entityManager){
// and public function __construct(Doctrine\ORM\EntityManager $entityManager){
}
src/Test/TestBundle/Classes/TestClass.php
namespace Test\TestBundle\Classes\TestClass;
use Test\TestBundle\ServiceUser\EntityManagerUser;
class TestClass extends EntityManagerUser{
/* currently no functions */
}
In my controller, line 84
$test= new TestClass;
// I tested that this throws the same error, it does // $test= new EntityManagerUser;
What have I missed?
Services only get their arguments if they are called through the service constructor:
$this->get('test.service_user.entity_manager_user');
Declaring the class as a service doenst make a difference if you create a new class and extend the original.
What you could do is also declare this new class as a service and still have it extend the base class.
test.classes.test_class:
class: Test\TestBundle\Classes\TestClass\TestClass
arguments: [ #doctrine.orm.entity_manager ]
then you dont have to define the constructor in the extended class because it is the parent.
then get the class by doing:
$testClass = $this->get('test.classes.test_class');
//will be instanceof Test\TestBundle\Classes\TestClass\TestClass
I worked out how to do what I wanted, which was not define entityManager each time I was required in an instanciated class.
It made sense to rename EntityManagerUser to ContainerListener, a static class, and inject #service_container into it through services, so it can then also return other classes.
namespace Test\TestBundle\EventListener;
class ContainerListener{
static $container;
// knock out the parent::onKernelRequest function that we don't want
public function onKernelRequest($event){
return;
}
public function __construct($container){
self::$container = $container;
}
static function twig(){
return self::$container->get('twig');
}
static function entityManager(){
return self::$container->get('doctrine')->getEntityManager();
}
static function entityManagerConnection(){
$entityManager = self::$container->get('doctrine')->getEntityManager();
return $entityManager->getConnection();
}
}
services.yml
services:
test.event_listener.container_listener:
class: Test\TestBundle\EventListener\ContainerListener
arguments: [ #service_container ]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
BaseClass.php gets entityManager
namespace Test\TestBundle\Class;
use Test\TestBundle\EventListener\ContainerListener;
class BaseClass{
public function __construct(){
$this->entityManager = ContainerListener::entityManager();
}
}
TestClass.php extends BaseClass as do others
class TestClass extends BaseClass(){
function someFunction(){
// etc etc
// $this->entityManager exists with no construct and without passing it
$stmt = $this->entityManager->getConnection()->prepare( $some_sql );
// etc etc
}
}
Somewhere in DefaultController.php
# nope # $entityManager = $this->getDoctrine()->getEntityManager();
# nope # $TestClass= new TestClass($entityManager);
$TestClass= new TestClass; # win!
I need to get doctrine working inside my helper, im trying to use like i normaly do in a controller:
$giftRepository = $this->getDoctrine( )->getRepository( 'DonePunctisBundle:Gift' );
But this gave me:
FATAL ERROR: CALL TO UNDEFINED METHOD
DONE\PUNCTISBUNDLE\HELPER\UTILITYHELPER::GETDOCTRINE() IN
/VAR/WWW/VHOSTS/PUNCTIS.COM/HTTPDOCS/SRC/DONE/PUNCTISBUNDLE/HELPER/UTILITYHELPER.PH
What Im missing here?
EDIT:
services file
services:
templating.helper.utility:
class: Done\PunctisBundle\Helper\UtilityHelper
arguments: [#service_container]
tags:
- { name: templating.helper, alias: utility }
Firts lines of helper file
<?php
namespace Done\PunctisBundle\Helper;
use Symfony\Component\Templating\Helper\Helper;
use Symfony\Component\Templating\EngineInterface;
class UtilityHelper extends Helper {
/*
* Dependency injection
*/
private $container;
public function __construct( $container )
{
$this->container = $container;
}
The problem here is that your Helper class is not container-aware; that is, it has no idea about all the services Symfony has loaded (monolog, twig, ...and doctrine).
You fix this by passing "doctrine" to it. This is called Dependency Injection, and is one of the core things that makes Symfony awesome. Here's how it works:
First, give your Helper class a place for the Doctrine service to live, and require it in the Helper's constructor:
class UtilityHelper
{
private $doctrine;
public function __construct($doctrine)
{
$this->doctrine = $doctrine;
}
public function doSomething()
{
// Do something here
}
}
Then, you use services.yml to define how Symfony should construct that instance of Helper:
services:
helper:
class: Done\PunctisBundle\Helper\UtilityHelper
arguments: [#doctrine]
In this case, #doctrine is a placeholder that means "insert the Doctrine service here".
So now, in your Controller, or in anything else that is container-aware, you can get access to Doctrine through the Helper class like this:
class SomeController()
{
public function someAction()
{
$this->get("helper")->doctrine->getRepository(...);
}
}
EDIT
After looking at your edit, it appears that you're injecting the entire service container into the Helper class. That's not a best practice -- you should only inject what you need. However, you can still do it:
services.yml
services:
helper:
class: Done\PunctisBundle\Helper\UtilityHelper
arguments: [#service_container]
UtilityHelper.php
class UtilityHelper
{
private $container;
public function __construct($container)
{
$this->container = $container;
}
public function doSomething()
{
// This won't work, because UtilityHelper doesn't have a getDoctrine() method:
// $this->getDoctrine()->getRepository(...)
// Instead, think about what you have access to...
$container = $this->container;
// Now, you need to get Doctrine
// This won't work... getDoctrine() is a shortcut method, available only in a Controller
// $container->getDoctrine()->getRepository(...)
$container->get("doctrine")->getRepository(...)
}
}
I've included a few comments there that highlight some common pitfalls. Hope this helps.
In Helper, Services etc you cannot use it like in actions.
You need to pass it like argument to youre Helper via service description in conf file(services.yml or *.xml).
Example:
services:
%service_name%:
class: %path_to_youre_helper_class%
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: %name% }
And dont forget catch it in __construct of youre Helper.
Example:
use Doctrine\ORM\EntityManager;
....
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
You can use it like:
public function myMethod()
{
$repo = $this->em->getRepository('DonePunctisBundle:Gift');
}