Symfony2 components, class instances - php

Question is why
$config = array(__DIR__ . '/app/config');
$locator = new Symfony\Component\Config\FileLocator($config);
$loader = new Symfony\Component\Routing\Loader\YamlFileLoader($locator);
$routes_collection = $loader->load('routes.yml');
Here $routes_collection is an instance of Symfony\Component\Routing\RouteCollection, and this code works fine.
And here
# config.yml
file_locator:
class: Symfony\Component\Config\FileLocator
arguments: ['%app_root%/app/config']
route_collection:
class: Symfony\Component\Routing\Loader\YamlFileLoader
arguments: ["#file_locator"]
calls:
- [load, ['routes.yml']]
#app.php
$routes_collection = $container->get('route_collection')
$routes_collection is an instance of Symfony\Component\Routing\Loader\YamlFileLoader and if i use it with Symfony\Component\Routing\Matcher\UrlMatcher am getting:
Argument 1 passed to Symfony\Component\Routing\Matcher\UrlMatcher::__construct() must be an instance of Symfony\Component\Routing\RouteCollection, instance of Symfony\Component\Routing\Loader\YamlFileLoader given.
Update
#Pazi ツ, but how to be if i want to use route_collection in matcher
matcher:
class: Symfony\Component\Routing\Matcher\UrlMatcher
arguments: ["#route_collection", "#context"]

If you want to take route_collection via config you need to define route_collection yaml_file_loader as the factory service of route_collection:
# config.yml
file_locator:
class: Symfony\Component\Config\FileLocator
arguments: ['%app_root%/app/config']
yaml_file_loader:
class: Symfony\Copmonent\Routing\Loader\YamlFileLoader
arguments: [ "#file_locator" ]
route_collection:
class: Symfony\Component\Routing\RouteCollection
factory_service: yaml_file_loader
factory_method: load
arguments: [ 'routes.yml' ]
This way you can do
$routes_collection = $container->get('route_collection');

Of course your service is an instance of YamlFileLoader because you configured this in the class attribute. The return value of the methods calls doesn't influence the instance type. You have to call the method in php, if you want to get the return value.
# config.yml
file_locator:
class: Symfony\Component\Config\FileLocator
arguments: ['%app_root%/app/config']
yaml_file_loader:
class: Symfony\Component\Routing\Loader\YamlFileLoader
arguments: ["#file_locator"]
#app.php
$routes_collection = $container->get('yaml_file_loader')->load('routes.yml');

Related

Autowiring problem with parameter injection in Symfony 5

I'm having troubles trying to retrieve a manager from a controller in Symfony 5.
I've got this MailerManager in src/Manager/MailerManager.php:
<?php
namespace App\Manager;
use App\Client\MailjetClient;
class MailerManager {
private $mailjetClient;
function __construct(MailjetClient $mailjetClient) {
$this->setMailjetClient($mailjetClient);
}
function send($data) {
}
function getMailjetClient() {
return $this->mailjetClient;
}
private function setMailjetClient($mailjetClient) {
$this->mailjetClient = $mailjetClient;
}
}
This manager needs to inject src/Client/MailjetClient.php in order to work, that has got this code:
<?php
namespace App\Client;
use \Mailjet\Resources;
class MailjetClient {
private $client;
function __construct(string $apikey, string $apisecret) {
$this->setClient($apikey, $apisecret);
}
function getClient() {
return $this->client;
}
function setClient($apikey, $apisecret) {
$this->client = new \Mailjet\Client($apikey, $apisecret);
}
}
This is just a wrapper for the mailjet sdk installed via composer, that needs to be feeded with different $apikey and $apisecret depending on the environment, for what I'm using parameters through services.yaml file, where I also have got autowiring enabled and service definitions for both MailjetClient and MailerManager:
parameters:
rabbitmq:
host: '%env(RABBITMQ_HOST)%'
port: '%env(RABBITMQ_PORT)%'
user: '%env(RABBITMQ_USER)%'
pwd: '%env(RABBITMQ_PWD)%'
mailjet:
apikey: '%env(MAILJET_APIKEY)%'
apisecret: '%env(MAILJET_APISECRET)%'
services:
_defaults:
autowire: true
autoconfigure: true
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
App\Client\MailjetClient\:
resource: '../src/Client/MailjetClient.php'
arguments:
$apikey: '%mailjet.host%'
$apisecret: '%mailjet.port%'
App\Manager\MailerManager\:
resource: '../src/Manager/MailerManager.php'
arguments:
$mailjetClient: '#client.mailjet'
The problem I'm having is that I'm getting this error: Cannot autowire service "App\Client\MailjetClient": argument "$apikey" of method "__construct()" is type-hinted "string", you should configure its value explicitly. when I try to inject the MailManager in the src/Controller/MailerController.php controller:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use App\Manager\MailerManager;
class MailerController extends AbstractController
{
/**
* #Route("/compose", name="compose")
*/
public function compose(MailerManager $mailerManager)
{
dump($mailerManager);die();
}
}
}
What could posibbly be wrong? I'm coming from Symfony 2, and this parameter injection was something standard that used to work like a charm, now I'm totally confused about how to mix the autowiring with the service manual definition.
I had a total disaster on the services.yml side, thanks to u_mulder and Tejas Gosai hints I could finally put this to work. I had a few type errors on the parameters I was injecting, the MailManager.php specific declaration was not needed, and my client namespaces should not end with \ ad u_mulder suggested.
Finally, with this services.yml it works:
parameters:
rabbitmq.host: '%env(RABBITMQ_HOST)%'
rabbitmq.port: '%env(RABBITMQ_PORT)%'
rabbitmq.user: '%env(RABBITMQ_USER)%'
rabbitmq.pwd: '%env(RABBITMQ_PWD)%'
mailjet.apikey: '%env(MAILJET_APIKEY)%'
mailjet.apisecret: '%env(MAILJET_APISECRET)%'
services:
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
App\Client\MailjetClient:
arguments:
$apikey: '%mailjet.apikey%'
$apisecret: '%mailjet.apisecret%'
App\Client\RabbitClient:
arguments: ['%rabbitmq.host%','%rabbitmq.port%','%rabbitmq.user%','%rabbitmq.pwd%']

Sylius/Symfony 3 inject service in a service

I created a service to extend the menu in admin of Sylius. It's work well ;)
I follow the official doc
I try to inject the router service in, but I've this following error :
Type error: Too few arguments to function
XXMenuListener::__construct(), 0 passed in
appDevDebugProjectContainer.php on line 1542 and exactly 1 expected
The declaration of this service :
services:
app.listener.admin.menu_builder:
class: XXX\Menu\AdminMenuListener
autowire: true
arguments:
- '#router'
tags:
- { name: kernel.event_listener, event: sylius.menu.admin.main, method: addAdminMenuItems }
and the service himself :
<?php
namespace XXX\Menu;
use Sylius\Bundle\UiBundle\Menu\Event\MenuBuilderEvent;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
final class AdminMenuListener
{
private $router;
public function __construct(Router $router){
$this->router = $router;
}
/**
* #param MenuBuilderEvent $event
*/
public function addAdminMenuItems(MenuBuilderEvent $event){
$menu = $event->getMenu();
$newSubmenu = $menu
->addChild('new')
->setLabel('XXX')
;
$newSubmenu
->addChild('new-subitem')
->setLabel('XXX')
//->setUri('https://www.google.com');
->setUri($this->router->generate('foo'))
;
}
}
What is wrong in ? Thanks for your help!
I think you need to clear cache if not helped to clean the cache directory manually.
In any case, you don't need a router service because menubuilder already has it.
For example:
for uri
$newSubmenu
->addChild('new-subitem')
->setLabel('XXX')
->setUri('https://www.google.com')
;
for route
$newSubmenu
->addChild('new-subitem', ['route' => 'foo'])
->setLabel('XXX')
;
If you use autowire to true you don't need to specify the router service. Something like this should be enough :
services:
app.listener.admin.menu_builder:
class: XXX\Menu\AdminMenuListener
autowire: true
tags:
- { name: kernel.event_listener, event: sylius.menu.admin.main, method: addAdminMenuItems }
In any case, your error indicates that you don't have any arguments. May be it's a caching issue or may be you have another service declaration for the same class XXX\Menu\AdminMenuListener without autowire to true and without arguments.

Non-existent service error since symfony 3.3

I had 2 working services at my symfony 3.2.(8?) project and had to up to 3.3 (currently 3.3.2). One of my services is working just fine, and the second one is giving me error:
services.yml
parameters:
#parameter_name: value
services:
_defaults:
autowire: true
autoconfigure: true
public: false
AppBundle\:
resource: '../../src/AppBundle/*'
exclude: '../../src/AppBundle/{Entity,Repository}'
list_brands:
class: AppBundle\Service\ListBrands
arguments: [ '#doctrine.orm.entity_manager' ]
calls:
- method: getBrands
picture_upload:
class: AppBundle\Service\UploadPicture
arguments: ['#kernel']
src\AppBundle\Service\UploadPicture.php
<?php
namespace AppBundle\Service;
use DateTime;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpKernel\Kernel;
class UploadPicture
{
protected $kernel;
public function __construct(Kernel $kernel)
{
$this->kernel = $kernel;
}
public function uploadPicture($object, string $oldPic, string $path)
{
/** #var UploadedFile $image */
$image = $object->getImage();
$time = new DateTime('now');
if ($image) {
$imgPath = '/../web/' . $path;
$filename = $time->format('d-m-Y-s') . md5($time->format('s')) . uniqid();
$image->move($this->kernel->getRootDir() . $imgPath,$filename . '.png');
$object->setImage($path . $filename . '.png');
} else {
$object->setImage($oldPic);
}
}
}
Error: You have requested a non-existent service "picture_upload".
Calling it like this: $uploadService = $this->get('picture_upload');
You have not written how you inject / call your service, but calling $this->get() sounds like a call from within a controller. I guess this is related to the new change in Symfony and your default service config of the public attribute.
Please check the following commented lines in your configuration:
# services.yml
services:
_defaults:
autowire: true
autoconfigure: true
public: false # here you are setting all service per default to be private
AppBundle\:
resource: '../../src/AppBundle/*'
exclude: '../../src/AppBundle/{Entity,Repository}'
list_brands:
class: AppBundle\Service\ListBrands
arguments: [ '#doctrine.orm.entity_manager' ]
calls:
- method: getBrands
picture_upload:
class: AppBundle\Service\UploadPicture
arguments: ['#kernel']
public: true # you need to explicitly set the service to public
You need to mark the service as public, either per default (not recommended) or explicitly in the service definition.

Calling services by factory methods

Right now I can call services from controllers in the following way:
$this -> get('myservice') -> doSomething();
But I want to do this according to the factory method pattern, like this:
ServiceFactory :: createMyservice() -> doSomething();
The problem is that I can't pass parameters with the second method, they're null.
services.yml:
parameters:
services:
myservice:
class: AppBundle\Service\Myservice
factory: [Appbundle\Factory\ServiceFactory, createMyservice]
arguments:
- "%kernel.root_dir%"
ServiceFactory.php:
<?php
namespace AppBundle\Factory;
use AppBundle\Service\MyService;
class ServiceFactory
{
public static function createMyservice($rootDir)
{
var_dump($rootDir); // NULL
$object = new Myservice($rootDir);
return $object;
}
}
What's wrong?

Symfony 2 error: ....must be an instance of Symfony\Component\Security\Http\HttpUtils in services.yml

I am passing an argument from services.yml to an authentication handler, and getting this error:
....must be an instance of Symfony\Component\Security\Http\HttpUtils
This is the class:
class AuthenticationFailure extends DefaultAuthenticationFailureHandler
{
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
I have tried all of these but none work:
#http_kernel
#request
#security.access.decision_manager
#security.context
#security.authentication.manager
#security.secure_random
#security.firewall
#service_container
What am I missing?
So I just need to know what service do I pass that is an instance of Symfony\Component\Security\Http\HttpUtils
Try this:
services:
http.utils.class:
class: Symfony\Component\Security\Http\HttpUtils
security.authentication.your_success_handler:
class: %security.authentication.success_handler.class%
public: false
arguments: [#http.utils.class, [], ...]

Categories