Doctrine 2 CLI commands from within PHP - doctrine

Hey all. I am writing a program that will transform some data in our database, and then call Doctrine to build YAML files from said Mysql Database structure. I have Doctrine working from within PHP. However I can't figure out how to call the CLI commands from within PHP. Following is the Doctrine 2 CLI command that does what I need.
php ./doctrine orm:convert-mapping --filter="users" --from-database yml ./test
This command works from the Linux command line, but how to I do this same thing via Doctrine objects? I don't want to just use the PHP exec statement to send a command to the shell. I wish to use the Doctrine object model.

Don!:
Apparently this is not a very common programming method. However, I have used Doctrine from PHP by calling it via the PHP EXEC command. I know you said that you would not like to do it this way. However, it actually works quite well. Below is an example of such a solution.
$cmd_string = "php ./doctrine orm:generate-entities --generate-annotations=1 --regenerate-entities=1 $this->entity_file_dir";
$result = array();
exec($cmd_string, &$result);
Hope this helps,
-Don!

I stumbled upon this question when trying to execute a command directly from a PHP script, without using the CLI.
Particularly, I was needing to call orm:ensure-production-settings.
Each Doctrine command has its own class: http://www.doctrine-project.org/api/orm/2.4/namespace-Doctrine.ORM.Tools.Console.Command.html
I solved it the following way:
$entityManager = ...; // Get the entity manager somehow in your application
// Creates the helper set
$helperSet = \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager);
// Initializes the desired command and sets the helper set
// In your case it should be ConvertMappingCommand instead
$command = new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand();
$command->setHelperSet($helperSet);
// Initializes the input
// Alternatives: http://api.symfony.com/2.0/Symfony/Component/Console/Input.html
$input = new \Symfony\Component\Console\Input\ArgvInput(); // Input coming from the CLI arguments
// Initializes the output
// Alternatives: http://api.symfony.com/2.0/Symfony/Component/Console/Output.html
$output = new \Symfony\Component\Console\Output\ConsoleOutput(); // Prints the output in the console
// Runs the command
$command->run($input, $output);
I'm new to Doctrine so I'm not exactly sure how this works, but it does. Any comment is appreciated.

Related

How to get Artisan::call('command:name') output in a variable?

Is it possible to get $output as follows:
$output = Artisan::call('command:name');
I have tried many solutions in different posts but it didn't work on my Laravel 5.2.
You can call the output method on Artisan
Artisan::call('command:name');
$output = Artisan::output();
Make sure you are using one of the available output methods like $this->line('output'); in the actual Artisan command. More info in the docs.
There are several ways to accomplish this, but as your are using such old version of Laravel maybe the best for your case is one that will not require a rewrite when you finally migrate to a newer version. Have you tried perhaps the vanilla PHP methods system, shell_exec and passthru?
system will return just the last line of the command on succes and FALSE on failure
shell_exec will return the entire output but without a way to fetch the output status code
passthru will not return any output instead it will output all on stdout. Although it can be put into a variable using output cache methods (i.e. ob_start and ob_get_contents)
In any case you should call those methods using as argument the CLI version of the command you wish to run:
$output = shell_exec("php artisan command:here");
P.S. If you by any chance have a user input that you want to pass as parameter to a artisan command, make sure you escape it with escapeshellcmd first.

Php artisan - custom command

I would like to create my own php artisan command so when I write
php artisan env:[variable]
I want to get that variable, so I can check fast if I'm getting the right variable from .env file
Is this possible? How to do it?
You can create a console command with:
php artisan make:console
As per the docs.
You are probably better off creating a static signature and passing in the field you want to return.
protected $signature = 'env:return {field}';
This will allow you to type php artisan env:return APP_DEBUG.
You can retrieve the input like so:
public function handle()
{
$envVariable = $this->argument('field');
//
}
You can then write some code to output the field, perhaps something like:
$this->info(env($envVariable));
This could all be condensed down into something like the below, as an example only:
protected $signature = 'env:return {field}';
public function handle()
{
$this->info(env($this->argument('field')));
}
I would suggest you read the docs I linked above as this will help you understand what is happening here a lot more.
This should get you started though, and you can then shape it to be exactly what you need.

How to debug Symfony command using Xdebug and phpStorm?

First of all let's start saying I am running the whole project in a Docker container and that means I don't have anything installed on the host.
Having that I am trying to debug a Symfony command which is kind of CLI script with the difference it's not being called as php script.php but as bin/console command.
What does the console file has inside it? See below:
#!/usr/bin/env php
<?php
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Debug\Debug;
set_time_limit(0);
/** #var Composer\Autoload\ClassLoader $loader */
$loader = require __DIR__.'/../app/autoload.php';
$input = new ArgvInput();
$env = $input->getParameterOption(['--env', '-e'], getenv('SYMFONY_ENV') ?: 'dev');
$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(['--no-debug', '']) && $env !== 'prod';
if ($debug) {
    Debug::enable();
}
$kernel = new AppKernel($env, $debug);
$application = new Application($kernel);
$application->run($input);
I have Xdebug installed and configured, I have phpStorm configured as well and that means using the browser (and the extension to enable/disable) I can debug whatever I want.
Typically this is how the command is executed from the console (bash):
<path_to_symfony>/bin/console quote:notify -d 120 -e <some_email>
Options are optional here and that means I can call the same as:
<path_to_symfony>/bin/console quote:notify
It will work both ways. I am using the docs from here to debug such command. Now this is how I am setting up the IDE to debug a Symfony command:
Go to Run/Debug Configuration
Click on Add button (little green + symbol)
Choose PHP Script
Name it as SF Command
At File box I wrote bin/console
At Arguments box I wrote quote:notify (which is my command)
But the IDE says first: Error: file not specified or invalid and then when I try to execute the command it says it's not a valid configuration. (also notice the little "X" which should means something is wrong)
What I am missing here? Can I get some help here? I am using the latest version of phpStorm.

Laravel - call Artisan command and wait for the result

I'm trying to call in a controller a very time-consuming Artisan command (it executes in 20-90 s ), but I have two problems. First of all it seems like the command does not execute at all (if I return the output it just returns "0").
Secondly the other part (returning the file) does not wait for the command to execute (but it can be related to the first part). Here's my code:
public function returnZip()
{
// just a failsafe, in case if scheduled command did not created the file yet
if( ! file_exists( storage_path( '/app/exports/' . date('Y_m_d') . '.zip' ) ) ){
Artisan::call('maximus:export');
}
return response()->file( storage_path( '/app/exports/' . date('Y_m_d') . '.zip' ) );
}
How can I properly execute the Artisan command from route/controller and wait for it to finish it's task?
EDIT
I tried to debug this problem a little more and found out that the command is not being executed at all when called from a route/controller.
Tried this:
Route::get('/test', function(){
Artisan::call('maximus:export');
return ['ok'];
});
And my command is supposed to create a file:
public function handle()
{
exec('touch /some/path/storage/app/exports/test');
}
When I run this command in terminal, the file is being created, but when I hit the route, it isn't. Any ideas?
Okay, I fired up Laravel and tested it. My command is:
public function handle()
{
exec('touch ' . storage_path(str_random(16) . '.txt'));
}
It works perfectly both in terminal and in a route by calling Artisan::call().
A wild guess: does the www-data user (or whatever user the PHP used by your webserver is running as) have sufficient privilege to write the file?
I'm pretty sure that the artisan command is being handled asynchronously, so it isn't going to wait for the command to finish. Therefore your response is likely to be empty/malformed.
You may want to look into Events and Listeners to ensuring your order of operations is correct (https://laravel.com/docs/5.4/events).
For example, in your maximus:export command you could fire an event immediately after the file has been created.
Example:
Create an event called ZipCreated and a listener called SendZip. Then in your artisan command handler call the event:
event(new ZipCreated($file));
Then link it to a listener in your EventServiceProvider.php:
protected $listen = [
Events\Repository\ZipCreated::class => [
Listeners\Repository\SendZip::class,
],
];
This way ZipCreated will provide SendZip with the zipped file (or a filepath if you want) and SendZip can handle returning the file to the user.
Now whenever the command is run, the creation of the file and the handling of response will always happen in the correct order.
if I return the output it just returns "0"
Let me point out just in case that the call method doesn't return the output of the command, but its exit code ("0" meaning success). Instead, Artisan::output() will return the output.
I'd say check the logs to see what's going on, also check you are in fact useing the Artisan facade. Otherwise, try a debugger or alternatively insert informative dd() statements ;) (the entry point being Illuminate\Foundation\Console\Kernel::call).
try this:
dd(Artisan::output());

How to send an argument when using Artisan::call?

I'm trying to run an Artisan command with an argument, but I can't figure out how. How do you do it? If I run php artisan video:webmtomp4 N in the terminal, it works fine.
Artisan::call(
'video:webmtomp4',
[$data['videoId']],
new StreamOutput(fopen(storage_path() . '/logs/artisan.log', 'w'))
);
How do you send an argument to the command when using Artisan::call? $data['videoId'] is set, so that is not a problem.
Changing my comment to an answer after checking the documentation. :)
The parameters array should be associative, e.g. ['argument' => $data['videoId']],.

Resources