Symfony Command have an empty name in production environement - symfony

I'm working on a Symfony2 Command, and I have an error when I try to run the command in prod environment, but it work perfectly in dev :
Everything is fine if I run :
php app/console mycommand:start
When I run
php app/console mycommand:start --env=prod
I get this error :
[LogicException]
The command defined in "NameSpace\Of\MyClassCommand" cannot have an empty name.
Command definition :
services:
my_service.command:
class: NameSpace\Of\MyClassCommand
arguments:
name: mycommand:start
tags:
- { name: console.command }
Command :
class MyClassCommand extends Command
{
/**
* #param InputInterface $input
* #param OutputInterface $output
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// logic
}
...
}
As you may notice, I don't overload the __construct or the configure methods. I tried but it didn't change anything. Same thing if I clean the cache.
Does anyone have an idea of what could be the difference between the prod and the dev environment, to cause this problem ?
Thank you

Clearing cache is not working for me. I need to declare base class as abstract class to hint Symfony not to register it as command.
abstract class BaseCommand {
...
}

I finally found the solution, Ruslan you were right : clear the cache.
But I had to manually nuke the cache folder, the cache:clear command was not enough.

Related

Symfony 4 disable profiler in controller action

I have an action in the controller for mass instert in the database...
So this uses a lot of resources and the profiler is caching everything and server goes down.
How can i disable the profiler (and all the debug services) in one action on the controller?
The controller looks like :
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use App\Sync\Incomming\Syncronize;
/**
* #Route("/sync")
*/
class SyncController extends AbstractController
{
private $syncronize;
public function __construct(Syncronize $syncronize)
{
$this->syncronize = $syncronize;
}
/**
* #Route("/",name="sync_index")
*/
public function index(Request $request, Profiler $profiler)
{
$message = "Hello";
return $this->render( 'sync/output.html.twig', ['message' => $message ]);
}
}
if I try to autowire the profiler in the constructor method then I get the error public function __construct(Syncronize $syncronize, Profiler $profiler):
Cannot autowire service "App\Controller\SyncController": argument
"$profiler" of method "__construct()" references class
"Symfony\Component\HttpKernel\Profiler\Profiler" but no such service
exists. You should maybe alias this class to the existing "profiler"
service.
if I try to autowire the profiler in the index method then I get the error public function index(Request $request, Profiler $profiler):
Controller "App\Controller\SyncController::index()" requires that you
provide a value for the "$profiler" 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.
EDIT
For big queries disabling the profiler was not the solution... Actually you need to disable the setSQLLogger:
$em->getConnection()->getConfiguration()->setSQLLogger(null);
Symfony 3.4 / 4
https://symfony.com/doc/4.0/profiler/matchers.html
use Symfony\Component\HttpKernel\Profiler\Profiler;
class DefaultController
{
// ...
public function someMethod(Profiler $profiler)
{
// for this particular controller action, the profiler is disabled
$profiler->disable();
// ...
}
}
If you have an error with autowiring
# config/services.yaml
services:
Symfony\Component\HttpKernel\Profiler\Profiler: '#profiler'
Old:
If you want to disable the profiler from a controller, you can like this:
use Symfony\Component\HttpKernel\Profiler\Profiler;
// ...
class DefaultController
{
// ...
public function someMethod(Profiler $profiler)
{
// for this particular controller action, the profiler is disabled
$profiler->disable();
// ...
}
}
Source: https://symfony.com/doc/current/profiler/matchers.html
Another way would be to use: $this->get('profiler')->disable();
Older:
Simply switch the app to prod mode and disable debug mode.
To do this, open the .env file on the server in your favourite editor (Note: You should never commit this file to Git, as you store secrets in there!).
In that file, you should see a section starting with: ###> symfony/framework-bundle ###
Just below that there is a APP_ENV=dev and APP_DEBUG=1, change these two lines to APP_ENV=prod and APP_DEBUG=0. Then save the file.
Next you should clear the cache for prod mode and install the assets. To do this, run the following commands:
php bin/console cache:clear --env=prod --no-debug
php bin/console cache:warmup --env=prod --no-debug
php bin/console assets:install --env=prod --no-debug --symlink
If you now load the application, it is in prod mode, which includes more caching and is faster as debug is disabled.
Note:
There will still be a timelimit for PHP. If you still hit that limit, you can either change your PHP setting or alternatively you could run the import from CLI, as CLI usually has no timelimit. If users need to be able to upload on their own, I'd suggest having them upload the file, enter a "note" about the file to a db and have a cronjob reading that db for not imported files and import them.

PHPUnit No such file or directory

I'm trying to test my RoomRepository with PHPUnit and Symfony 4. I installed symfony/phpunit-bridge using composer. I created a simple entity called Room with one id and name attributs and a repository method to get a Room by its id.
public function get(int $id): ?Room
{
/** #var Room $room */
$room = $this->findOneBy(['id' => $id]);
return $room;
}
My test is quite simple as you can see :
public function testGet(): void
{
/** #var RoomRepository $repository */
$repository = $this->em->getRepository(Room::class);
$room = $repository->get(1);
$this->assertCount(1, $room);
}
I am new with test and I don't know if it's the right way to proceed. I followed the Symfony documentation.
So, when I execute the following command :
./vendor/bin/simple-phpunit
I am getting this error :
Doctrine\DBAL\Exception\ConnectionException: An exception occurred in driver: SQLSTATE[HY000] [2002] No such file or directory
I am pretty sure this is a commun mistake and very easy to fix...
Furthermore, I wrote other simple asserts that worked very well. I don't think it's about PHPUnit configuration.
Here some informations about my env :
PHP 7.1
Symfony4.0.5
PHPUnit 5.7.27
Docker with Laradock (containers : mysql, apache2, workspace)
Thanks guys for reading my post and have a nice day :)
I had the same problem – I just forgot to add the database url to the phpunit.xml.dist file. You have to add:
<phpunit ...>
<php>
...
<env name="DATABASE_URL" value="mysql://username:password#server:port/database" />
...
</php>
...
</phpunit>
Of course with your own credentials instead of the placeholders.

Symfony2: How do I clear all of Symfony's (and all 3rd party plugins) cache in 1 command?

so I've been testing Doctrine queries and other Symfony code and I have to run several commands just to clear Doctrine/Symfony caches. I was searching for the net and came across another command to clear Assetic assets/etc.
From what I've read
php app/console cache:clear
will only clear Symfony cache. it won't include Doctrine and perhaps more.
I know I can create a bash script to clear all my caches but this obviously means I know all the "clear cache" commands. I only found out about the Assetic clear cache/assets by accident. What about those I don't know?
So is there 1 "clear cache" command that can do it for me? This will have to include Symfony/Doctrine/Assetic/Twig and whatever plugins I have installed.
Thanks a lot
What you are looking for is highly dependent on the developer of the bundle that uses the cache. Not even doctrine, that comes with the standard version of symfony has its cache clear command integrated. But what you can do is extend the default symfony command with a listener that runs all the cache clear command you want like this:
<?php
namespace DefaultBundle\Event\Listener;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Process\Process;
class CacheClearListener implements CacheClearerInterface
{
private $environment;
/**
* #return array
*/
private static function getCommands()
{
return array(
'php ./app/console doctrine:cache:clear-metadata --no-debug --flush',
'php ./app/console doctrine:cache:clear-query --no-debug --flush',
'php ./app/console doctrine:cache:clear-result --no-debug --flush'
);
}
public function clear($cacheDir)
{
$output = new ConsoleOutput();
$output->writeln('');
$output->writeln('<info>Clearing Doctrine cache</info>');
foreach (self::getCommands() as $command) {
$command .= ' --env='.$this->environment;
$success = $this->executeCommand($command, $output);
if (!$success) {
$output->writeln(sprintf('<info>An error occurs when running: %s!</info>', $command));
exit(1);
}
}
}
/**
* #param string $command
* #param ConsoleOutput $output
*
* #return bool
*/
private function executeCommand($command, ConsoleOutput $output)
{
$p = new Process($command);
$p->setTimeout(null);
$p->run(
function ($type, $data) use ($output) {
$output->write($data, false, OutputInterface::OUTPUT_RAW);
}
);
if (!$p->isSuccessful()) {
return false;
}
$output->writeln('');
return true;
}
/**
* #param Kernel $kernel
*/
public function setKernel(Kernel $kernel)
{
$this->environment = $kernel->getEnvironment();
}
}
Register the listener like this:
<service id="cache_clear_listener" class="DefaultBundle\Event\Listener\CacheClearListener">
<call method="setKernel">
<argument type="service" id="kernel"/>
</call>
<tag name="kernel.cache_clearer" priority="254" />
</service>
And that is all. Now all you need to do is keep adding your new cache clear command to the getCommands() method. In order to find this commands you can run something like
php app/console | grep cache
to see all available commands that contain the word "cache" in them
After your listener is set, every time you run php app/console cache:clear it will trigger all the command that you listed in the getCommands() method of your listener.
Hope this helps,
Alexandru

Issue when trying to reload the fixtures

When I try to reload my fixtures using
php app/console doctrine:fixtures:load
I'm getting this error:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or
update a parent row: a foreign key constraint fails (foo_db.Book, CONSTRAINT FK_C29271DD816C6140 FOREIGN KEY (author_id) REFERENCES Author (id))
The error is showed when the status "> purging database" is showed.
This is my code:
class Book{
...
/**
* #ORM\ManyToOne(targetEntity="Author", inversedBy="books")
*/
private $author;
...
}
class Author{
...
/**
* #ORM\OneToMany(targetEntity="Book", mappedBy="author")
*/
private $books;
}
More: my boss has the same code and it doesn't have that error.
Any idea?
sf 2.0.1 (just updated)/ubuntu 10.10.
If I'm guessing correctly, you are using a MySQL database. If yes, then you are facing a bug/problem with the current version of the doctrine-fixtures library for Doctrine2. The problem is that they are using the TRUNCATE command to purge the current database values but this command has problem deleting foreign associations in MySQL.
See this issue and this one on the GitHub repository of the library for more information and workarounds.
In my particular case, I run this command from a script, so to make the command work correctly, I do:
php app/console doctrine:database:drop --force
php app/console doctrine:database:create
php app/console doctrine:schema:update --force
php app/console doctrine:fixtures:load --append
This way, the purging is done by the drop command and appending has the same effect as not appending since the database is empty when the fixtures are loaded.
I must admit I don't know why your boss doesn't have the problem, maybe there is no book associated with an author in his database.
Hope this help.
Regards,
Matt
I've created a simple Event Subscriber class for Symfony 4. All you need to fix the self-referencing foreign keys issue is to add the below class somewhere to your Symfony 4 project.
This subscriber fires before each Symfony CLI command. In case if the command's name is doctrine:fixtures:load, it performs database purge, but doing SET FOREIGN_KEY_CHECKS = 0 first.
This solves the issue without any other modification.
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ConsoleCommandEventSubscriber implements EventSubscriberInterface
{
/**
* #var EntityManagerInterface
*/
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public static function getSubscribedEvents()
{
return [
ConsoleEvents::COMMAND => 'onCommand',
];
}
public function onCommand(ConsoleCommandEvent $event)
{
if ($event->getCommand()->getName() === 'doctrine:fixtures:load') {
$this->runCustomTruncate();
}
}
private function runCustomTruncate()
{
$connection = $this->entityManager->getConnection();
$connection->exec('SET FOREIGN_KEY_CHECKS = 0');
$purger = new ORMPurger($this->entityManager);
$purger->setPurgeMode(ORMPurger::PURGE_MODE_DELETE);
$purger->purge();
$connection->exec('SET FOREIGN_KEY_CHECKS = 1');
}
}
Add to your composer.json new section script
"scripts": {
"load-fixtures": [
"bin/console doctrine:database:drop --if-exists --force",
"bin/console doctrine:database:create",
"bin/console doctrine:mi:m",
"bin/console doctrine:fixtures:load"
]
}
Then you can run composer install && composer load-fixtures

Symfony2 custom console command not working

I created a new Class in src/MaintenanceBundle/Command, named it GreetCommand.php and put the following code in it:
<?php
namespace SK2\MaintenanceBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class GreetCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('maintenance:greet')
->setDescription('Greet someone')
->addArgument('name', InputArgument::OPTIONAL, 'Who do you want to greet?')
->addOption('yell', null, InputOption::VALUE_NONE, 'If set, the task will yell in uppercase letters')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
if ($name) {
$text = 'Hello '.$name;
} else {
$text = 'Hello';
}
if ($input->getOption('yell')) {
$text = strtoupper($text);
}
$output->writeln($text);
}
}
?>
And tried to call it via
app/console maintenance:greet Fabien
But i always get the following error:
[InvalidArgumentException]
There are no commands defined in the "maintenance" namespace.
Any ideas?
I had this problem, and it was because the name of my PHP class and file didn't end with Command.
Symfony will automatically register commands which end with Command and are in the Command directory of a bundle. If you'd like to manually register your command, this cookbook entry may help: http://symfony.com/doc/current/cookbook/console/commands_as_services.html
I had a similar problem and figured out another possible solution:
If you override the default __construct method the Command will not be auto-registered by Symfony, so you have to either take the service approach as mentioned earlier or remove the __construct override and make that init step in the execute method or in the configure method.
Does actually anyone know a good best practice how to do init "stuff" in Symfony commands?
It took me a moment to figure this out.
I figured out why it was not working: I simply forgot to register the Bundle in the AppKernel.php. However, the other proposed answers are relevant and might be helpful to resolve other situations!
By convention: the commands files need to reside in a bundle's command directory and have a name ending with Command.
in AppKernel.php
public function registerBundles()
{
$bundles = [
...
new MaintenanceBundle\MaintenanceBundle(),
];
return $bundles;
}
In addition to MonocroM's answer, I had the same issue with my command and was silently ignored by Symfony only because my command's constructor had 1 required argument.
I just removed it and call the parent __construct() method (Symfony 2.7) and it worked well ;)
If you are over-riding the command constructor and are using lazy-loading/autowiring, then your commands will not be automatically registered. To fix this you can add a $defaultName variable:
class SunshineCommand extends Command
{
protected static $defaultName = 'app:sunshine';
// ...
}
Link to the Symfony docs.
I think you have to call parent::configure() in your configure method
I had this same error when I tried to test my command execution with PHPUnit.
This was due to a wrong class import :
use Symfony\Component\Console\Application;
should be
use Symfony\Bundle\FrameworkBundle\Console\Application;
cf. Other stack thread
In my case it was complaining about the "workflow" namespace although the WorkflowDumpCommand was correctly provided by the framework.
However, it was not available to run because I have not defined any workflows so the isEnabled() method of the command returned false.
I tried to use a service passed via constructor inside the configure method:
class SomeCommand extends Command {
private $service;
public function __construct(SomeService $service) {
$this->service = $service;
}
protected function configure(): void {
$this->service->doSomething(); // DOES NOT WORK
}
}
Symfony uses Autoconfiguration that automatically inject dependencies into your services and register your services as Command, event,....
So first just make sure that you have services.yaml in your config folder. with autoconfigure:true.
this is the default setting
Then Make sure That All your files are exactly the same name as Your Class.
so if you have SimpleClass your file must be SimpleClass.php
If you have a problem because of a __constructor,
go to services.yml and add something like this:
app.email_handler_command:
class: AppBundle\Command\EmailHandlerCommand
arguments:
- '#doctrine.orm.entity_manager'
- '#app.email_handler_service'
tags:
- { name: console.command }
For newer Symfony-Version (5+) commands must be registered as services.
What I do frequently forget while setting it up, is to tag it properly:
<service id="someServiceCommand">
<tag name="console.command"/>
</service>
Without this litte adaptation, your command name will not be displayed and therefore not accessible.

Resources