Is it possible to dump a database using doctrine 2? I have read that symfony has a library which extends doctrine to do it but How could I use it in my zendframework project with Bisna Doctrine 2 Integration?
For Symfony2:
Type
php app/console doctrine:schema:create --dump-sql
in the command line
This is an old thread but I was just doing something similar in Symfony and decided to develop an actual command for it. That's more of a Symfony way of doing it and gives you more control on the output as well as allowing you access to the parameters, so you don't have to parse Yaml using bash script :)
namespace Fancy\Command;
use Fancy\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
class DatabaseDumpCommand extends AbstractCommand
{
/** #var OutputInterface */
private $output;
/** #var InputInterface */
private $input;
private $database;
private $username;
private $password;
private $path;
/** filesystem utility */
private $fs;
protected function configure()
{
$this->setName('fancy-pants:database:dump')
->setDescription('Dump database.')
->addArgument('file', InputArgument::REQUIRED, 'Absolute path for the file you need to dump database to.');
}
/**
* #param InputInterface $input
* #param OutputInterface $output
* #return int|null|void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->output = $output;
$this->database = $this->getContainer()->getParameter('database_name') ;
$this->username = $this->getContainer()->getParameter('database_user') ;
$this->password = $this->getContainer()->getParameter('database_password') ;
$this->path = $input->getArgument('file') ;
$this->fs = new Filesystem() ;
$this->output->writeln(sprintf('<comment>Dumping <fg=green>%s</fg=green> to <fg=green>%s</fg=green> </comment>', $this->database, $this->path ));
$this->createDirectoryIfRequired();
$this->dumpDatabase();
$output->writeln('<comment>All done.</comment>');
}
private function createDirectoryIfRequired() {
if (! $this->fs->exists($this->path)){
$this->fs->mkdir(dirname($this->path));
}
}
private function dumpDatabase()
{
$cmd = sprintf('mysqldump -B %s -u %s --password=%s' // > %s'
, $this->database
, $this->username
, $this->password
);
$result = $this->runCommand($cmd);
if($result['exit_status'] > 0) {
throw new \Exception('Could not dump database: ' . var_export($result['output'], true));
}
$this->fs->dumpFile($this->path, $result);
}
/**
* Runs a system command, returns the output, what more do you NEED?
*
* #param $command
* #param $streamOutput
* #param $outputInterface mixed
* #return array
*/
protected function runCommand($command)
{
$command .=" >&1";
exec($command, $output, $exit_status);
return array(
"output" => $output
, "exit_status" => $exit_status
);
}
}
and AbstractCommand is just a class that extends symfony's ContainerAwareCommand:
namespace Fancy\Command;
use Symfony\Component\HttpFoundation\Request;
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;
abstract class AbstractCommand extends ContainerAwareCommand
{
}
Doctrine has no database-dump feature.
I agree it would be nice, but it's also not the ORM's goal.
You could dump the database using
a PHP script
a system mysqldump
phpMyAdmin
Here's an article explaining those solutions.
I created a small script that read the parameters from app/config/parameters.yml and output all the data from a MySQL database to a file (with current datetime used as name).
Save this in the root of your Symfony project (e.g. mysqldump.sh):
#!/bin/bash
# See http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in/23905052#23905052
ROOT=$(readlink -f $(dirname "$0"))
cd $ROOT
# Get database parameters
dbname=$(grep "database_name" ./app/config/parameters.yml | cut -d " " -f 6)
dbuser=$(grep "database_user" ./app/config/parameters.yml | cut -d " " -f 6)
dbpassword=$(grep "database_password" ./app/config/parameters.yml | cut -d " " -f 6)
filename="$(date '+%Y-%m-%d_%H-%M-%S').sql"
echo "Export $dbname database"
mysqldump -B "$dbname" -u "$dbuser" --password="$dbpassword" > "$filename"
echo "Output file :"
ls -lh "$filename"
Result when running the script:
$ bash mysqldump.sh
Export […] database
Warning: Using a password on the command line interface can be insecure.
Output file :
-rw-rw-r-- 1 […] […] 1,8M march 1 14:39 2016-03-01_14-39-08.sql
Depend on your database. if you use mysql, create a php command to utilise mysqldump
like running this
mysqldump -u YourUser -p YourDatabaseName > wantedsqlfile.sql
For a more generic doctrine way:
protected function execute(InputInterface $input, OutputInterface $output)
{
$conn = $this->getDoctrineConnection('default');
$path = $input->getArgument('filepath');
if (! is_dir(dirname($path))) {
$fs = new Filesystem();
$fs->mkdir(dirname($path));
}
$cmd = sprintf('mysqldump -u %s --password=%s %s %s > %s',
$conn->getUsername(),
$conn->getPassword(),
$conn->getDatabase(),
implode(' ', ['variables', 'config']),
$path
);
exec($cmd, $output, $exit_status);
}
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));
}
}
When using the Laravel5 scheduler:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
We receive the following default output if no command is ready to run:
# No scheduled commands are ready to run.
How to disable this default Laravel5 message? We don't want to have an output if there is no command ready to run. The best would be, when we were able to configure that message and return code on our self.
You can create a new command in app/Console/Commands similar to below, which extends the default schedule:run command.
It overrides the handle method while leaving everything else as-is to avoid having Laravel output the "No scheduled commands are ready to run." line when it didn't do anything.
By using a different name there's no need to worry about conflicts, and you can still run the original php artisan schedule:run command at any time if you so desire.
<?php
namespace App\Console\Commands
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Console\Scheduling\ScheduleRunCommand;
class RunTasks extends ScheduleRunCommand
{
/**
* The console command name.
*
* #var string
*/
protected $name = 'run:tasks';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Custom task runner with no default output';
/**
* Create a new command instance.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
public function __construct(Schedule $schedule)
{
parent::__construct($schedule);
}
/**
* Execute the console command.
*
* #return void
*/
public function handle()
{
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
continue;
}
if ($event->onOneServer) {
$this->runSingleServerEvent($event);
} else {
$this->runEvent($event);
}
$this->eventsRan = true;
}
if (! $this->eventsRan) {
// Laravel would output the default text here. You can remove
// this if statement entirely if you don't want output.
//
// Alternatively, define some custom output with:
// $this->info("My custom 'nothing ran' message");
}
}
}
Verify that Laravel sees your new command:
php artisan | grep run:tasks
Finally update your cron to run the new command:
* * * * * cd /path-to-your-project && php artisan run:tasks >> /dev/null 2>&1
As I mentioned in the comments I see two possibilities
You can filter the output by removing what you don't want
* * * * * cd /path-to-your-project && php artisan schedule:run | awk '{ if (/No scheduled commands are ready to run./ && !seen) { seen = 1 } else print }'
Or you can override with your own command:
$ php artisan make:command ScheduleRunCommand
By making your own command (mostly copy/past from ScheduleRunCommand) or extending the ScheduleRunCommand as #dave-s proposed
And if you want to still run php artisan schedule:run with your new command,
you need to register it in a service provider
$this->app->extend('schedule.run', function () {
return new \App\Console\Commands\ScheduleRunCommand;
});
If you look at the code for Laravel at https://github.com/laravel/framework/blob/78505345f2a34b865a980cefbd103d8eb839eedf/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php#L82
public function handle()
{
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
continue;
}
if ($event->onOneServer) {
$this->runSingleServerEvent($event);
} else {
$this->runEvent($event);
}
$this->eventsRan = true;
}
if (! $this->eventsRan) {
$this->info('No scheduled commands are ready to run.');
}
}
You see that it's handled via $this->info handler.
The info handler is defined in Command.php Which calls the line method, which calls the output handler which is defined in the run command
So in essence to be able to intercept this you should be able to override the OutputStyle which is based on the symfonystyle by binding your own output handler before running the commands in the file you call in your cron job.
The best working scenario I can think of is by using an OutputFormatter where you simply return null when the string matches your target string.
$this->output->setFormatter( new MyCatchemAllFormatter() );
And in the class you would define something along the lines of:
use Symfony\Component\Console\Formatter\OutputFormatter;
class MyCatchemAllFormatter extends OutputFormatter
{
public function formatAndWrap(string $message, int $width)
{
if($message != 'No scheduled commands are ready to run.') {
return parent::formatAndWrap($message, $width);
}
return null;
}
}
I understand that my solution is DIRTY and I'll get downvotes by most of SO users, but it's quick to do without registering additional service providers, modifying classes and etc.
I've checked sources and found this at line 81 of ScheduleRunCommand which is
public function handle()
{
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
continue;
}
if ($event->onOneServer) {
$this->runSingleServerEvent($event);
} else {
$this->runEvent($event);
}
$this->eventsRan = true;
}
if (! $this->eventsRan) { // L81
$this->info('No scheduled commands are ready to run.'); // L82
}
}
The quickest way to "cheat" with it is to copy that class to app/Console/ScheduleRunCommand.php and copy that file to original source path every-time when composer dump-autoload called.
1) Copy original file to app/Console folder:
cp vendor/laravel/framework/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php patch/ScheduleRunCommand.php app/Console/ScheduleRunCommand.php
2) add such line in composer.json scripts:post-autoload-dump section:
cp app/Console/ScheduleRunCommand.php vendor/laravel/framework/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php
3) modify Your message in app/Console/ScheduleRunCommand.php (L82):
if (! $this->eventsRan) {
$this->info('NOTHING');
}
4) run: composer dump
and result:
I'm testing a web crawler script. I'm using the php builtin webserver to test against pages locally.
I can start the server but I cannot kill the process because it is already killed (I get the exception that I set Could not kill the testing web server).
Here is my attempt:
<?php
use Behat\Behat\Tester\Exception\PendingException;
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Behat\Hook\Scope\AfterScenarioScope;
/**
* Defines application features from the specific context.
*/
class FeatureContext implements Context, SnippetAcceptingContext
{
const TESTING_BASE_URL = 'http://127.0.0.1:6666';
const TESTING_DIR = '/tmp/testDirectory';
private $pid;
/**
* Initializes context.
*
* Every scenario gets its own context instance.
* You can also pass arbitrary arguments to the
* context constructor through behat.yml.
*/
public function __construct()
{
}
/**
* #BeforeScenario
*/
public function before(BeforeScenarioScope $scope)
{
// Create testing directory holding our pages
if (!is_dir(self::TESTING_DIR)) {
if (!mkdir(self::TESTING_DIR)) {
throw new \Exception('Cannot create the directory for testing');
}
}
// Start the testing server
$command = sprintf(
'php -S %s -t $%s >/dev/null 2>&1 & echo $!',
escapeshellarg(self::TESTING_BASE_URL),
escapeshellarg(self::TESTING_DIR)
);
$output = [];
exec($command, $output, $return_var);
if ($return_var !== 0) {
throw new \Exception('Cannot start the testing web server');
}
$this->pid = (int)$output[0];
echo sprintf(
'Testing web server started on %s with PID %s %s %s',
self::TESTING_BASE_URL,
(string)$this->pid,
PHP_EOL,
PHP_EOL
);
}
/**
* #AfterScenario
*/
public function after(AfterScenarioScope $scope)
{
// ... kill the web server
$output = [];
exec('kill ' . (string) $this->pid, $return_var);
if ($return_var !== 0) {
throw new \Exception('Could not kill the testing web server (PID ' . (string) $this->pid . ')');
}
echo 'Testing web server killed (PID ', (string) $this->pid, ')', PHP_EOL, PHP_EOL;
// ... remove the test directory
$o = [];
exec('rm -rf ' . escapeshellarg(self::TESTING_DIR), $o, $returnVar);
if ($returnVar !== 0) {
throw new \Exception('Cannot remove the testing directory');
}
}
// ...
}
I also tried various things like putting it all in the constructor, using register_shutdown_function, without any success.
What am I missing? Any idea on how I can solve this?
Instead of just "not caring about killing the server process" (because to me it looks like it's gone when I try to kill the process, hence the error, I can't find it when I issue ps aux | grep php on the command line after running behat), isn't it "cleaner" to kill it as I attend to?
The exec call is missing the output parameter:
exec('kill ' . (string) $this->pid, $output, $return_var);
Unless this is set, the exception will always be thrown, because $return_var is actually the output of the command (which is an array not an integer).
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);
}
}
}
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