Call a Command from a Controller not work - symfony

i have this code to run schema update command from controller i got help from symfony document
i have this code:
namespace AdminBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class DefaultController extends Controller
{
/**
* #Route("/admin")
*/
public function indexAction(KernelInterface $kernel)
{
$application = new Application($kernal);
$input = new ArrayInput(array(
'command' => 'doctrine:schema:update'
));
$output = new NullOutput();
$application->run($input, $output);
return new Response("");
}
}
it's not work for me i get this error after open this url (http://127.0.0.1:8000/admin):
Controller "AdminBundle\Controller\DefaultController::indexAction()" requires that you provide a value for the "$kernel" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
how can i do?

Instead of injecting the KernelInterface $kernel directly into your action (I guess, you're not using it as a declared service), call your container directly for asking the kernel:
public function indexAction()
{
$kernel = $this->get('kernel');
$application = new Application($kernel); // btw: you have an typo in here ($kernal vs $kernel)
$input = new ArrayInput(array(
'command' => 'doctrine:schema:update'
));
$output = new NullOutput();
$application->run($input, $output);
// tip: use "204 No Content" to indicate "It's successful, and empty"
return new Response("", 204);
}

While Michael's answer works, it is not the preferred method in Symfony 3.3, which had several changes to dependency injection. Your code will actually work just fine with some changes to your services configuration.
As the documentation states, the Dependency Injection Container changed in Symfony 3.3, and by default your controllers are registered as services:
# app/config/services.yml
services:
# ...
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
This allows you to autowire the kernel through arguments in your controller action method, like you tried. The reason yours isn't working is because your AdminBundle is likely not set up the way your AppBundle is by default in app/config/services.yml. To truly solve the issue in the way that Symfony 3.3 wants, you should add AdminBundle to your services configuration like so:
# app/config/services.yml
services:
# add this below your AppBundle\Controller definition
AdminBundle\Controller\:
resource: '../../src/AdminBundle/Controller'
public: true
tags: ['controller.service_arguments']
With that, you no longer have to call $this->get('kernel');, and your original code will work as you have it, with KernelInterface as a parameter to your action method.
Furthermore, you can extend the new AbstractController instead of the regular Controller, and then calls to $this->get() will not work anymore, which is the way Symfony is going.
So again while Michael's answer will work just fine, I would advise you to implement the answer I've given simply because Symfony 3.3 prefers that method going forward.

Related

symfony serializer jmsserializerbundle service name conflict

I have the following issue: I am working on a symfony (2.8) project which depends on the jmsserializerbundle (1.1).
When enabling the symfony-serializer alongside the jms-serializer package,
# app/config/config.yml
framework:
# ...
serializer: { enabled: true }
jms_serializer:
metadata:
#...
upon calling $this->get('serializer') or $this->get('jms_serializer') I only get the jms-serializer. This issue seems to have been resolved in jmsserializerbundle version 2.0: https://github.com/schmittjoh/JMSSerializerBundle/issues/558
Is there any way to solve this without updating jmsserializerbundle to 2.0?
Would there be any difference in performance compared to the normal symfony-serializer configuration, when wrapping a symfony-serializer in a custom service? like so:
<?php
use SomeCustomNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;
class SerializerService implements SerializerInterface
{
private $serializer;
public function __construct()
{
$this->serializer = new Serializer(
[new SomeCustomNormalizer(), new ObjectNormalizer()],
[new JsonEncode()]
);
}
public function serialize($data, $format, array $context = array())
{
# serialize
}
public function deserialize($data, $type, $format, array $context = array())
{
# deserialize
}
}
# SomeBundle/Resources/config/services.yml
serializer_service:
class: SomeBundle\SerializerService
The question regarding the performance came up for me because the existing jms configuration registers the jmsserializerbundle in the app kernel, which is not the case my custom service, which is just set up in services.yml.
Thanks in advance
Solution
As described below I just had to add one line to the jms-config:
# app/config/config.yml
jms_serializer:
enable_short_alias: false
metadata:
#...
Is there any way to solve this without updating jmsserializerbundle to 2.0?
JMS Serializer provides the option:
jms_serializer:
enable_short_alias: false
Would there be any difference in performance compared to the normal symfony-serializer configuration when wrapping a Symfony-serializer in a custom service? like so:
I guess not, the Symfony serializer is just 'another' service defined by the FrameworkBundle, a wrapper around the Serializer class with the normalizers and encoders injected.
If you create your own service (like in your example) it will be compiled by the service container as well. You can check the definition here: https://github.com/symfony/symfony/blob/v2.8.52/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml

Read from parameters in Symfony 3.4 getParameter null

I can't read a parameter from parameters.yml in my controller.
I want to do this:
//My Controller
class ExampleController extends Controller
{
function someMethod($argument)
{
dump($this->getParameter('free_proxy'));die();
and in parameters.yml I got:
parameters:
free_proxy: "http://xxx:8080"
I get an error: Call to a member function getParameter() on null
I've tested some solutions like adding some services and using get and stuff but nothing works.
EDIT: also, I tried this:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
bind:
$freeProxy: '%free_proxy%'
Then using:
$this->container->getParameter('free_proxy');
But I got an error: Unused binding "$freeProxy" in service...
So there are two mysteries here. First is why is the container not being injected which in turn causes getParameter to fail. And second, why does bind generate that unused binding error.
You did not show your routing but I suspect that somewhere along the line you actually have:
$exampleController = new ExampleController();
If so then this explains why getParameter is failing. You really need to let Symfony create the controller based on the route. Otherwise the container is not injected and other controller magic is skipped.
I installed a fresh 3.4 app with the old directory structure and added a parameter
composer create-project symfony/framework-standard-edition s34
# app/config/parameters.yml
parameters:
free_proxy: "http://xxx:8080"
I then tweaked the default controller using the default route annotation:
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
$freeProxy = $this->getParameter('free_proxy');
// replace this example code with whatever you need
return $this->render('default/index.html.twig', [
'base_dir' => realpath($this->getParameter('kernel.project_dir')).DIRECTORY_SEPARATOR.$freeProxy,
]);
}
}
And everything worked as expected. The Symfony request handler takes care of injecting the container and thus gives you access to the parameters. If you cannot get this working then please update your question with your routing information.
I then took a look at the bind issue. You really want to inject these parameters instead of pulling them. I updated services.yml
# app/config/services.yml
services:
bind:
$freeProxy: '%free_proxy%'
And started getting those unused binding errors. It turns out that bind does not work for action injection. Not really sure why. I don't use it much but I really would have expected that just adding $freeProxy to your action method would work. In any event, here is a working example of the proper way to do things.
class ExampleController extends Controller
{
private $freeProxy;
public function __construct($freeProxy)
{
$this->freeProxy = $freeProxy;
}
/**
* #Route("/example", name="example")
*/
function someMethod()
{
dump($this->freeProxy);
dump($this->getParameter('free_proxy'));die();
}
}
I then went to a fresh 4.2 project and tried action injection:
class IndexController extends AbstractController
{
public function index($freeProxy)
{
return new Response("Index $freeProxy");
}
}
Action injection works as expected for 4.2 but not 3.4. Constructor injection works fine in either version.
documentation show like this :
parameters.yml :
parameters:
mailer.transport: sendmail
to set :
$container->setParameter('mailer.transport', 'sendmail');
to get :
$container->getParameter('mailer.transport');

use SessionLogoutHandler to add data in database

i have a symfony application where a logout () is not manually :
( in file routing.yml)
logout:
pattern: /logout
and i would like when i click on /logout we must be able to record information in the database and for that i use :
SessionLogoutHandler class
here is my code :
namespace Symfony\Component\Security\Http\Logout;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Compta\MyappliBundle\Entity\LogTache;
use Compta\MyappliBundle\Entity\User;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class SessionLogoutHandler implements LogoutHandlerInterface
{
public function logout(Request $request, Response $response, TokenInterface $token)
{
$em = $this->getDoctrine()->getEntityManager();
$user= $this->get('security.context')->getToken()->getUser();
$u = new LogTache();
$u->setDate(new \DateTime());
$u->setUser($user->getUsername());
$u->setActioneffectue('Déconnexion');
$em->flush($u);
$request->getSession()->invalidate();
} }
when i click on /logout i get an error :
Fatal error: Call to undefined method
Symfony\Component\Security\Http\Logout\SessionLogoutHandler::getDoctrine()
in
C:\wamp\www\MyAppli\vendor\symfony\src\Symfony\Component\Security\Http\Logout\SessionLogoutHandler.php
on line 46
how to solve it ?
thank in advance
excuse my english
You don't have access to the container from that class. The $this->getDoctrine()... and $this->get() is more likely to be found in a controller action.
You can add a constructor and pass in '#doctrine' and '#security.context' as arguments from the service definition, or with Symfony 3.3 type-hint the appropriate classes (for Doctrine - Doctrine\Common\Persistence\ObjectManager) and use them within the class.
Symfony 2.6 deprecated the security.context service, so it's also quote likely you are running a older version of the framework. Upgrading may bring additional benefits, such as the v3.3+ auto-wiring of services.

Service in symfony2 - how service file should look like?

I am trying to create service in symfony2 which will verify if session contains certain information and if not redirect the user to another controller. I want this piece of code to work as a service as I will be using it in many controllers.
I have problem as manual on Symfony2 book does not provide information how service file should look like. Should it be a normal php class?
Please find below dump of my files with information on error that I receive.
In \AppBundle\Services I create file my_isbookchosencheck.php containing:
<?php
namespace AppBundle\my_isbookchosencheck;
class my_isbookchosencheck
{
public function __construct();
{
$session = new Session();
$session->getFlashBag()->add('msg', 'No book choosen. Redirected to proper form');
if(!$session->get("App_Books_Chosen_Lp")) return new RedirectResponse($this->generateUrl('app_listbooks'));
}
}
My service.yml:
my_isbookchosencheck:
class: AppBundle\Services\my_isbookchosencheck
My conntroller file:
/**
* This code is aimed at checking if the book is choseen and therefore whether any further works may be carried out
*/
$checker = $this->get('my_isbookchosencheck');
Error:
FileLoaderLoadException in FileLoader.php line 125: There is no extension able to load the configuration for "my_isbookchosencheck" (in C:/wamp/www/symfony_learn/app/config\services.yml). Looked for namespace "my_isbookchosencheck", found "framework", "security", "twig", "monolog", "swiftmailer", "assetic", "doctrine", "sensio_framework_extra", "fos_user", "knp_paginator", "genemu_form", "debug", "acme_demo", "web_profiler", "sensio_distribution" in C:/wamp/www/symfony_learn/app/config\services.yml (which is being imported from "C:/wamp/www/symfony_learn/app/config\config.yml").
There are few mistakes that you made, which I am going to explain in short, and I will give you an example of the service you want to create.
You created your service in AppBundle\Services, yet your namespace is registered differently - namespace AppBundle\Services\my_isbookchosencheck;. It should be namespace AppBundle\Services;. I would also advise you to use singular names when creating directories - in this case Service would be better, instead of Services.
You're using your __constructor directly to apply some logic and return the result of it. Better way would be to create a custom method, which could be accessed when necessary.
You're creating new instance of Session which means that you wont be able to access anything that was previously added and stored in session. The right way here, would be to inject RequestStack which holds the current Request and get the session from there.
I believe you also registered your service wrong. In your services.yml file, it should be under services: option. This is why you got the error you pasted.
So, let's see how your service should like.
services.yml
services:
book_service:
class: AppBundle\Service\BookService
arguments:
- #request_stack
- #router
BookService.php
namespace AppBundle\Service;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\RouterInterface;
class BookService {
/* #var $request Request */
private $request;
/* #var $router RouterInterface */
private $router;
public function __construct(RequestStack $requestStack, RouterInterface $router) {
$this->request = $requestStack->getCurrentRequest();
$this->router = $router;
}
public function isBookChoosen() {
$session = $this->request->getSession();
// Now you can access session the proper way.
// If anything was added in session from your controller
// you can access it here as well.
// Apply your logic here and use $this->router->generate()
}
}
Now in your controller you can simply use it like this:
$this->get('book_service')->isBookChoosen()
Well this is a short example, but I hope you got the idea.
try
services:
my_isbookchosencheck:
class: AppBundle\Services\my_isbookchosencheck
in your services.yml, and check that you use the correct namespaces.
Your Class is fine and it should work, however may i suggest that you use
symfony2 session service instead of creating the session object yourself, you can pass it as a constructor argument:
<?php
// namespace edited
namespace AppBundle\Services;
use Symfony\Component\HttpFoundation\Session\Session;
class my_isbookchosencheck
{
public function __construct(Session $session);
{
$session->getFlashBag()->add('msg', 'No book choosen. Redirected to proper form');
if(!$session->get("App_Books_Chosen_Lp")) return new RedirectResponse($this->generateUrl('app_listbooks'));
}
}
and then edit your services.yml accordingly, so the service container will inject the session object:
services:
my_isbookchosencheck:
class: AppBundle\Services\my_isbookchosencheck
arguments: [#session]
Also check out his question on so:
How do you access a users session from a service in Symfony2?
Services are just regular PHP classes, nothing special. But you must register it in order to be recognized by the system. Here are the steps how you do it,
Create a regular PHP class (you can inject other services if it requires)
namespace Acme\DemoBundle\Service;
class MyService
{
private $session;
public function _construct(SessionInterface $session /* here we're injecting the session service which implements the SessionInterface */)
{
$this->session = $session;
}
// other methods go here, which holds the business logic of this class
}
ok, we created a class, we need to register it to be able to use it by service container, here how you do it:
the simplest way is to put it into config.yml file, like this:
services:
my_service:
class: Acme\DemoBundle\Service\MyService
arguments:
- #session
or, another way, is to create a file (e.g. services.yml, may be in config folder), and import it inside the config.yml file (the content of the file is the same as the first way):
imports:
- { resource: services.yml }
or, you can create a services.yml(the content of the file is the same as the first way) file inside you bundle's Resources folder, specify it under the load method of your Extension class (under the DependencyInjection folder), (this way requires some special directory and file structure, read about it in the doc):
class AcmeDemoExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources'));
$loader->load('services.yml');
}
}
In you case, you're not registering your service, the service container just couldn't find it. Register it by one of the above ways.

Symfony2-How to use access a service from outside of a controller

In my Symfony2 controller, this works fine:
$uploadManager = $this->get('upload.upload_manager');
but when I move it to a custom Listener:
use Doctrine\ORM\Event\LifecycleEventArgs;
use Acme\UploadBundle\Upload\UploadManager;
class PersonChange
{
public function postRemove(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entityManager = $args->getEntityManager();
$uploadManager = $this->get('ep_upload.upload_manager');
echo "the upload dir is " . $uploadManager->getUploadDir();
}
}
I get an error:
Fatal error: Call to undefined method Acme\MainBundle\Listener\PersonChange::get() in /home/frank/...
I know I must need a use statement but don't know what to use.
Update: Defining controllers as services is no longer officially recommended in Symfony.
The get() method in the Controller class is just a helper method to get services from the container, and it was meant to get new Symfony2 developers up to speed faster. Once people get comfortable with the framework and dependency injection, it's recommended to define controllers as services and inject each required service explicitly.
Since your PersonChange class is not a controller and doesn't extend the Controller class, you don't have that get() helper method. Instead, you need to define your class as a service and inject needed services explicitly. Read the Service Container chapter for details.
As I ran into the exact same problem maybe I can help
What Elnur said is perfectly fine and I'll just try to pop up a real life example.
In my case I wanted to access
$lucenemanager = $this->get('ivory.lucene.manager')
Even by extending the controller I couldn't get it to work while the controller does access the container (I still did not understand why)
In config.yml my listener (searchindexer.listener) is declared as follow :
services:
searchindexer.listener:
class: ripr\WfBundle\Listener\SearchIndexer
arguments:
luceneSearch: "#ivory_lucene_search"
tags:
- { name: doctrine.event_listener, event: postPersist }
A service (ivory.lucene.search) is passed as argument in my service/listener.
Then in my class
protected $lucenemanager;
public function __construct($luceneSearch)
{
$this->lucenemanager = $luceneSearch;
}
Then you can use the get method against $this
An approach that always works, despite not being the best practice in OO
global $kernel;
$assetsManager = $kernel->getContainer()->get('acme_assets.assets_manager');‏
If you need to access a Service, define it in the class constructor:
class PersonChange{
protected $uploadManager;
public function __construct(UploadManager $uploadManager){
$this->uploadManager = $uploadManager;
}
// Now you can use $this->uploadManager.
}
Now you can pass the Service as argument when calling the class (example 1) or define the clas itself as a Service (recommended, example 2)
Example 1:
use Acme\PersonChange;
class appController{
function buzzAction(){
$uploadManager = $this->get('upload.upload_manager');
$personChange = new PersonChange($uploadManager);
Example 2 (better):
Define PersonChange as a Service itself, and define the other Service as an argument in services.yml file:
# app/config/services.yml
services:
upload.upload_manager:
class: AppBundle\uploadManager
PersonChange:
class: AppBundle\PersonChange
arguments: ['#upload.upload_manager']
In this way, you don't have to bother with the upload_manager service in the Controller, since it's implicitely passed as an argument for the constructor, so your Controller can be:
class appController{
function buzzAction(){
$personChange = $this->get('PersonChange');

Resources