I'm new to Symfony 4, I'm using profiler to debug in a dev environment.
I'm trying to debug a function in controller/ But I can't use a logger or a dump nothing appear.
<?php
namespace App\Controller;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Psr\Log\LoggerInterface;
class CRUDController extends Controller
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function batchActionDownload(ProxyQueryInterface $selectedModelQuery)
{
$this->logger->debug('exampleMethod :: $ourVar at interesting time', [
'our_var' => $selectedModelQuery
]);
var_dump($selectedModelQuery);
dump($selectedModelQuery);
return new RedirectResponse(
$this->admin->generateUrl('list', $this->admin->getFilterParameters())
);
}
I have no error, but simply my debug isn't showing up.
Any Idea of what I'm doing bad ?
Normally the best way to define the route is to use the annotation. You are new and i don't how know you have defined your route.
But if seems that your functions isn't called. So try to set a route and call it in your browser.
/**
* #Route("/mycrud", name="mycrud")
*/
public function batchActionDownload(ProxyQueryInterface $selectedModelQuery)
Then you should be able to open your function in the browser under http://yourdomain/mycrud. Then you should see the debug bar and your var_dump.
The next thing is you redirect in your function. So you output something and redirect. You can't see the output in that place. You have to stop the code before output with an exit for example.
Related
What would be a proper way to have a Service (that will be used by a Command) write information to the terminal? I like the same functionality to parse thousands of records to be able to be called from both a Controller and a Command. When it's called from the Command, I like to write a status to the terminal for every parsed record.
I've tried autowiring InputInterface $input, OutputInterface $output into the service but that gives me the following error:
Cannot autowire service "App\Service\Command\TestIoService": argument
"$input" of method "__construct()" references interface
"Symfony\Component\Console\Input\InputInterface" but no such service
exists. Did you create a class that implements this interface?
I could pass the input and output to the service but I was wondering if there is a better way to do this
I know this is over a year old, but I just stumbled upon the same requirement for my project. It's a bad idea to be passing around the Input/Output Interfaces.
Since Symfony 2.4 you've been able to use the Monolog component to log to the console. (https://symfony.com/doc/current/logging/monolog_console.html). It's already all wired-up for you. DI The LoggerInterface (use Psr\Log\LoggerInterface) in your constructor. Then use it like you normally would. $this->logger->debug('Hello World'). Your output is dependent on the verbosity level set via the CLI -v, -vv, -vvv options as described in the link above.
// src/Command/YourCommand.php
namespace App\Command;
use App\Service\AwesomeService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class YourCommand extends Command
{
public function __construct(AwesomeService $service)
{
$this->service = $service;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->service->doMethod();
}
}
// src/Service/AwesomeService.php
namespace App\Service;
use Psr\Log\LoggerInterface;
class AwesomeService
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
protected function doMethod()
{
$this->logger->debug('Some info');
$this->logger->notice('Some more info');
}
}
I've read about sessions here:
https://symfony.com/doc/current/components/http_foundation/sessions.html
If I use the code directly, Symfony logins using Guard don't work on the first attempt (first time always fails, second time succeeds). So I'm guessing that I have to use the session at $this->container->get('session'). But adding bags to that baffles me.
I've tried this:
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request) {
$session=$this->container->get('session');
$cart=new AttributeBag('ShoppingCartItems');
$session->registerBag($cart);
$session->start();
...but it tells me
Cannot register a bag when the session is already started.
I've tried this:
...in the bundle:
public function build(ContainerBuilder $container) {
parent::build($container);
$container->addCompilerPass(new \Website\DependencyInjection\Compiler\InjectShoppingCart());
}
...and then this for InjectShoppingCart
namespace Website\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
class InjectShoppingCart implements CompilerPassInterface {
public function process(ContainerBuilder $container) {
$bagDefinition = new Definition();
$bagDefinition->setClass(AttributeBag::class);
$bagDefinition->addArgument("ShoppingCartItems");
$bagDefinition->addMethodCall("setName", ["ShoppingCartItems"]);
$container->setDefinition("ShoppingCartItemsService",$bagDefinition);
$container->getDefinition("session")->addMethodCall("registerBag",[new Reference("ShoppingCartItemsService")]);
}
}
...which I tried to use, Cargo Cult style, from question 44723613. Whatever it does, it does not result in the registration of an attribute bag.
No matter what I've tried so far, either Guard authentication breaks, or else the bag doesn't get registered. I want to do both, though, hopefully without installing Megs and Megs of external libraries.
It can't be this hard. What have I missed?
Okay, I figured it out.
In the end, it was just one line from the example I was using that was making it fail for me:
$bagDefinition->addArgument("ShoppingCartItems");
...has to be deleted.
What this all amounts to is that in the compiler pass, instead of making it do this:
$bag = new AttributeBag('ShoppingCartItems');
$bag->setName('ShoppingCartItems');
$session->registerBag($bag);
I needed to make it do this:
$bag = new AttributeBag();
$bag->setName('ShoppingCartItems');
$session->registerBag($bag);
So InjectShoppingCart ends up looking like this:
namespace Website\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
class InjectShoppingCart implements CompilerPassInterface {
public function process(ContainerBuilder $container) {
$bagDefinition = new Definition();
$bagDefinition->setClass(AttributeBag::class);
$bagDefinition->addMethodCall("setName", ["ShoppingCartItems"]);
$container->setDefinition("ShoppingCartItemsService",$bagDefinition);
$container->getDefinition("session")->addMethodCall("registerBag",[new Reference("ShoppingCartItemsService")]);
}
}
I'm working on an EntityRepository class, and I need to dump some data to my log file. I can't use dump() because this isn't going to build a page; it's just going to return some JSON. Eventually.
Normally, in a Controller, I'd use:
$logger = $this->getLogger();
But I'm not in a Controller.
Thx for your help.
UPDATE: this is for forensic logging. I'm just using it for debugging purposes. It'll be removed afterwards.
I looked into this a bit. My first hunch is "Well, if you could define EntityRepositories as services, then that would make this easy because you could then just inject the logger"
But how do you inject the logger into repositories that doctrine is creating? It turns out you can specify your own repository factory
I'm going to assume all it needs is to implement the Doctrine\ORM\Repository\RepositoryFactory interface, but you'll probably want to subclass Doctrine\ORM\Repository\DefaultRepositoryFactory.
You will also need to create your own, base repository class that can hold a logger. Let's start there
src/AppBundle/Doctrine/EntityRepository.php
<?php
namespace AppBundle\Doctrine;
use Doctrine\ORM\EntityRepository;
use Psr\Log\LoggerInterface;
class LoggerAwareEntityRepository extends EntityRepository
{
protected $logger;
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
Now, the factory
src/AppBundle/Doctrine/LoggerAwareRepositoryFactory.php
<?php
namespace AppBundle\Doctrine;
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use AppBundle\Doctrine\LoggerAwareEntityRepository;
class LoggerAwareRepositoryFactory extends DefaultRepositoryFactory
{
protected $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
protected function createRepository(EntityManagerInterface $entityManager, $entityName)
{
$repository = parent::createRepository($entityManager, $entityName);
if ($repository instanceof LoggerAwareEntityRepository) {
$repository->setLogger($this->logger);
}
return $repository;
}
}
Now for the confguration glue to make it all work
app/config/services.yml
services:
logger_aware_repository_factory:
class: AppBundle\Doctrine\LoggerAwareRepositoryFactory
arguments: ['#logger']
app/config/config.yml
doctrine:
orm:
entity_managers:
default:
repository_factory: "#logger_aware_repository_factory"
Lastly, for the actual implementation
src/AppBundle/Entity/SomeCustomRepository.php
<?php
namespace AppBundle\Entity;
use AppBundle\Doctrine\LoggerAwareEntityRepository;
class SomeCustomRepository extends LoggerAwareEntityRepository
{
public function findSomethingCustom()
{
// Log something
$this->logger->log('message');
}
}
Full disclosure: this is untested code - there might be bugs!
Depending on what you want to log the most neat solution would be to create either a doctrine or doctrine entity listener, probably on post load. Inject the logger in the listener.
Once you decide you don't need it, just remove the listener.
Hi Have followed the instructions to enable the SensioFrameworkExtraBundle here: http://symfony.com/doc/2.1/bundles/SensioFrameworkExtraBundle/index.html
Thereafter I create the below controller:
namespace Acme\DemoBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class MyController
{
/**
* #Template
*/
public function indexAction()
{
}
}
If set up a route pointing to the indexAction on this controller and browse to it, I get the following error:
The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller?
It looks like the SensioFrameworkExtraBundle is not actually enabled, but I cannot figure out why. I'm looking for advice.
The #Template annotation is working. As the error suggest, you must return something. If return array, it will be sent to template engine. Make sure the template exists.
public function indexAction()
{
...
$somedata = 'fill data';
return array('somedata' => $somedata);
}
My point is to call a generic function in all my controllers in some Bundle (let's say AdminBundle). I got a login listener in whitch I set a session that contain true or false and I need to check this session before every method of my AdminBundle.
So I tried to make a __construct() function in my AdminBundle controllers, but it appears that I can't access to a service from this method (because the container is not yet loaded so I can't access $this).
The best practice should be to use a listener to call this service before very method of those controllers but I can't figure out whitch listener I need to use (cannot find a clue on google...).
Hope in clear enough, don't hesitate to ask questions if you don't understand my point !
Thanks in advance for any solution/idea (if you think that I'm not using the correct way to do it please explain my your point of view !)
After the afternoon on this issue i finally get a solution thanks to mahok.
For those whitch have the same issue here's my controller listener :
<?php
namespace Site\MyBundle\Listener;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\Routing\Router;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ControllerListener
{
protected $container;
protected $router;
public function __construct(ContainerInterface $container, Router $router)
{
$this->router = $router;
$this->container = $container;
}
public function onKernelController(FilterControllerEvent $event)
{
if (HttpKernel::MASTER_REQUEST == $event->getRequestType())
{
$controller = $event->getController();
$controller = $controller[0];
$new = new \ReflectionObject($controller);
if($new->getNamespaceName() == 'Site\MyBundle\Controller')
{
$test = $this->container->get('myservice')->test();
if(empty($test) || !$test)
{
$index = $this->router->generate('index');
$event->setController(function() use($index) {
return new RedirectResponse($index);
});
}
}
}
}
}
So basically it compare the namespace of your current controller's action with another and if true i check some variable to know if the user can be here or not.
Thanks again mahok you show me the way !