I want to set up an order allowing me to make clear: cache test mode, then do a drop database, drop scheama, add scheme, add fixtures in test mode.
class BaseCommand extends \Symfony\Component\Console\Command\Command {
//put your code here
protected function configure()
{
$this
->setName('mycommand:test')
->setDescription('Launch test')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$command_first_migration = $this->getApplication()->find('cache:clear');
$arguments_first_migration = array(
'command' => 'cache:clean',
'--env' => 'test'
);
$input_first_migration = new ArrayInput($arguments_first_migration);
try {
$returnCode = $command_first_migration->run($input_first_migration, $output);
} catch (\Doctrine\DBAL\Migrations\MigrationException $ex) {
echo "MigrationExcepion !!!! ";
}
}
}
but I have this result :
clearing the case for the dev environment with debug true
How to pass the test in dev environment?
thank you
You can't set the --env=test since the Kernel and the environment are already created when you run php app/console mycommand:test.
The only way is to specify the env when you run your command :
php app/console mycommand:test --env=test
Related
I have a small console command where I'd like to read some environment variables, but it does not seem to read the vars from the .env file or the server configs in the console (php file works)
The code
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$debug = $input->getArgument('debug') === 'y' ? true : false;
$this->project = $input->getArgument('project');
$start = new DateTime();
$debug ? $io->text('<fg=green>Starting upload</>') : null;
dump(getenv('APP_ENV'));
dump(getenv('MAILER_USERNAME'));
die;
...
}
Commands to test
php bin/console app:make-backup
Output:
Starting upload
false
false
php bin/console app:make-backup --env=prod
Output:
Starting upload
"prod"
false
php bin/console app:make-backup --env=dev
Output:
Starting upload
"dev"
false
.env File
APP_ENV=dev
MAILER_USERNAME=info#xxx.com
I don't see where I am doing wrong? Issue exists on nginx and apache server, BUT using getenv('MAILER_USERNAME') in any php-file works.
I think your best bet would be to go ahead and inject the env variables. Trying to access them directly can be a bit tricky.
# config/services.yaml
services:
App\Command\MyCommand:
$appEnv: '%env(APP_ENV)%'
$mailerUsername: '%env(MAILER_USERNAME)%'
# src\Command\MyCommand
class MyCommand extends Command {
protected static $defaultName = 'app:mine';
private $appEnv;
private $mailerUsername;
public function __construct($appEnv,$mailerUsername) {
parent::__construct();
$this->appEnv = $appEnv;
$this->mailerUsername = $mailerUsername;
}
protected function execute(InputInterface $input, OutputInterface $output) {
$output->writeln(("My Command " . $this->appEnv . " " . $this->mailerUsername));
}
}
I need to import a lot of data from a csv file (45 Mo) in myqsl database with Symfony. I imported League\Csv\Reader library
I made a command with doctrine.
It works but I is very slow.
How can I accelerate this ?
I tried to :
adding : $this->em->clear() after $this->em->flush();
adding : //Disable SQL Logging: to avoid huge memory loss.
$this->em->getConnection()->getConfiguration()->setSQLLogger(null);
.
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use App\Entity\Developer;
use App\Entity\BadgeLabel;
use Doctrine\ORM\EntityManagerInterface;
use League\Csv\Reader;
class CsvImportCommand extends Command
{
public function __construct(EntityManagerInterface $em){
parent::__construct();
$this->em = $em;
}
// the name of the command (the part after "bin/console")
protected static $defaultName = 'app:import-developpers';
protected function configure()
{
$this
// the short description shown while running "php bin/console list"
->setDescription('Import a new developper.')
// the full command description shown when running the command with
// the "--help" option
->setHelp('This command allows you to import a develpper...')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$io->title('Importation en cours');
$reader = Reader::createFromPath('%kernel.root_dir%/../src/Data/developers_big.csv')
->setHeaderOffset(0)
;
$results = $reader->getrecords();
$io->progressStart(iterator_count($results));
//Disable SQL Logging: to avoid huge memory loss.
$this->em->getConnection()->getConfiguration()->setSQLLogger(null);
foreach ($results as $row) {
$developer = $this->em->getRepository(Developer::class)
->findOneBy([
'firstName' => ($row['FIRSTNAME']),
'lastName'=> ($row['LASTNAME'])
])
;
if (null === $developer) {
$developer = new developer;
$developer
->setFirstName($row['FIRSTNAME'])
->setLastName($row['LASTNAME']);
$this->em->persist($developer);
$this->em->flush();
$this->em->clear();
}
$badgeLabel = $this->em->getRepository(BadgeLabel::class)
->findOneBy([
'name' => ($row['BADGE LABEL']),
'level'=> ($row['BADGE LEVEL'])
])
;
if (null === $badgeLabel) {
$badgeLabel = new BadgeLabel;
$badgeLabel
->setName($row['BADGE LABEL'])
->setLevel($row['BADGE LEVEL']);
$this->em->persist($badgeLabel);
$this->em->flush();
$this->em->clear();
}
$developer
->addBadgeLabel($badgeLabel);
$io->progressAdvance();
}
$this->em->flush();
$this->em->clear();
$io->progressFinish();
$io->success('Importation terminée avec succès');
}
}
The command works put its to slow. After 15 min, only 32% was updload in my Mysql database. I Expected it in 2 minutes max
Method1: (not the best)
When flush method is called, Symfony go throught all listeners. So, you could avoid to flush on each loop. You can replace each flush by this code:
if (0 === ($batchSize++ % $input->getOption('fetch'))) {
$this->entityManager->flush();
$this->entityManager->clear();
}
fetch option can be declared in configure method:
const BATCH_SIZE = 1000; // As example
/**
* Configure the command.
*/
protected function configure()
{
$this
// the short description shown while running "php bin/console list"
->setDescription('Import a new developper.')
//This option helps you to find a good value and use BATCH_SIZE constant as default
->addOption('fetch', 'f', InputArgument::OPTIONAL, 'Number of loop between each flush', self::BATCH_SIZE)
// the full command description shown when running the command with
// the "--help" option
->setHelp('This command allows you to import a develpper...')
;
Method2: More efficient
You can create a command which writes all SQL queries with update or insert in a sql file. Then, you launch a native command that read the files and execute queries.
Method3: Using DBAL
As suggested in comments, youcould use DBAL to avoid unnecessary object hydration with Doctrine.
After a slight modification of my units, I wanted the update with a simple php app/console doctrine: update --force. But no action executed and in addition no response. I then did a php app/check.php meaning me no problems (Your system is ready to run Symfony2 projects). I do not understand and it doesn't provide an error. Here's what I've done:
Command: ********: ***** ProjetSymphony $ php app / console***
Answer (none): ******* **** $ ProjetSymphony***
If someone has an idea.
Screen :
Try with:
php app/console doctrine:schema:update --force
Maybe it's only a syntaxis error.
Also, if anyone tries to run php app/console in a newer symfony version (for example symfony 3.0), you will get an error: no file found because the file was moved to 'bin' folder. Now to run from the console, you have to use php bin/console instead. Just in case this change confused anyone who started to learn symfony and updated to 3.0.
I finally found my mistake. I had a command file that prevented the execution of my order (CreateUserCommand.php)
If someone wants to explain to me why this cosait file an error during the execution of my order ...
Here is the file :
<?php
namespace FP\UserBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use FOS\UserBundle\Model\User;
use FOS\UserBundle\Command\CreateUserCommand as BaseCommand;
class CreateUserCommand extends BaseCommand
{
/**
* #see Command
*/
protected function configure()
{
exit;
echo "tes";
parent::configure();
$this
->setName('fp:user:create')
->getDefinition()->addArguments(array(
new InputArgument('age', InputArgument::REQUIRED, 'The age')
))
;
}
/**
* #see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
exit;
echo "tes";
$username = $input->getArgument('username');
$email = $input->getArgument('email');
$password = $input->getArgument('password');
$age = $input->getArgument('age');
$inactive = $input->getOption('inactive');
$superadmin = $input->getOption('super-admin');
$manipulator = $this->getContainer()->get('fos_user.util.user_manipulator');
$manipulator->setAge($age);
$manipulator->create($username, $password, $email, !$inactive, $superadmin);
$output->writeln(sprintf('Created user <comment>%s</comment>', $username));
}
/**
* #see Command
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
exit;
echo "tes";
parent::interact($input, $output);
if (!$input->getArgument('age')) {
$age = $this->getHelper('dialog')->askAndValidate(
$output,
'Please choose a age:',
function($age) {
if (empty($age)) {
throw new \Exception('Lastname can not be empty');
}
return $age;
}
);
$input->setArgument('age', $age);
}
}
}
I am trying to test my Symfony2 Console command using phpunit.
I'm following the Symfony2 cookbook article about this topic:
http://symfony.com/doc/current/components/console/helpers/questionhelper.html#testing-a-command-that-expects-input
However, if I fail to provide an input (a test fail), then phpunit simply sit there doing nothing waiting for input. Here's an example:
// MyCommand.php
class MyCommand extends Command {
// ... configure()
protected function execute(InputInterface $input, OutputInterface $output) {
$qh = $this->getHelper('question');
$q1 = new ConfirmationQuestion('First question, yes or no?', false);
$qh->ask($input, $output, $q);
$q2 = new ConfirmationQuestion('Second question, yes or no?', false);
$qh->ask($input, $output, $q);
}
}
// MyCommandTest.php
class MyCommandTest extends \PHPUnit_Framework_TestCase {
// ... getInputstream()
public function testExecute() {
$app = new Application();
$app->add(new MyCommand());
$cmd = $app->find('askquestions');
$cmdTester = new CommandTester($cmd);
$helper = $cmd->getHelper('question');
$helper->setInputStream($this->getInputStream('y\\n')); // this should be yy\\n
$cmdTester->execute([
'command' => $cmd->getName(),
]);
}
}
Please notice that I have purposefully made my test incorrect, it is only supplying an answer to question 1. Since I wrote that test, I have since added q2 but I forgot to modify my tests. Being a good programmer though I run phpunit to see if there are problems, but phpunit hangs as it expects input from q2!
How do I make it so my test will disregard any further requests for input, fail if it encounters one, and keep going with other tests?
Is there a way to run a console command from a Symfony 2 test case? I want to run the doctrine commands for creating and dropping schemas.
This documentation chapter explains how to run commands from different places. Mind, that using exec() for your needs is quite dirty solution...
The right way of executing console command in Symfony2 is as below:
Option one
use Symfony\Bundle\FrameworkBundle\Console\Application as App;
use Symfony\Component\Console\Tester\CommandTester;
class YourTest extends WebTestCase
{
public function setUp()
{
$kernel = $this->createKernel();
$kernel->boot();
$application = new App($kernel);
$application->add(new YourCommand());
$command = $application->find('your:command:name');
$commandTester = new CommandTester($command);
$commandTester->execute(array('command' => $command->getName()));
}
}
Option two
use Symfony\Component\Console\Input\StringInput;
use Symfony\Bundle\FrameworkBundle\Console\Application;
class YourClass extends WebTestCase
{
protected static $application;
public function setUp()
{
self::runCommand('your:command:name');
// you can also specify an environment:
// self::runCommand('your:command:name --env=test');
}
protected static function runCommand($command)
{
$command = sprintf('%s --quiet', $command);
return self::getApplication()->run(new StringInput($command));
}
protected static function getApplication()
{
if (null === self::$application) {
$client = static::createClient();
self::$application = new Application($client->getKernel());
self::$application->setAutoExit(false);
}
return self::$application;
}
}
P.S. Guys, don't shame Symfony2 with calling exec()...
The docs tell you the suggested way to do it. The example code is pasted below:
protected function execute(InputInterface $input, OutputInterface $output)
{
$command = $this->getApplication()->find('demo:greet');
$arguments = array(
'command' => 'demo:greet',
'name' => 'Fabien',
'--yell' => true,
);
$input = new ArrayInput($arguments);
$returnCode = $command->run($input, $output);
// ...
}
Yes, if your directory structure looks like
/symfony
/app
/src
then you would run
phpunit -c app/phpunit.xml.dist
from your unit tests you can run php commands either by using
passthru("php app/console [...]") (http://php.net/manual/en/function.passthru.php)
exec("php app/console [...]") (http://www.php.net/manual/en/function.exec.php)
or by putting the command in back ticks
php app/consode [...]
If you are running the unit tests from a directory other than symofny, you'll have to adjust the relative path to the app directory for it to work.
To run it from the app:
// the document root should be the web folder
$root = $_SERVER['DOCUMENT_ROOT'];
passthru("php $root/../app/console [...]");
The documentation has been updated since my last answer to reflect the proper Symfony 2 way of calling an existing command:
http://symfony.com/doc/current/components/console/introduction.html#calling-an-existing-command