I am writing a symfony console command which will be executable by using "php bin/console app:mycommand" (symfony documentation: https://symfony.com/doc/current/console.html#creating-a-command).
In my MyCommand class I need to use the getDoctrine-function, so I have to extend the controller, but I don't see a way how to do that. Any ideas?
Currently I get following error on CLI: Attempted to call an undefined method named "getDoctrine" of class "App\Command\MyCommand".
<?php
// src/Command/MyCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class MyCommand extends Command
{
// the name of the command (the part after "bin/console")
protected static $defaultName = 'app:mycommand';
protected function configure()
{
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// Not working, producing mentioned error
$em = $this->getDoctrine()->getManager();
}
}
?>
The getDoctrine() method is provided by the ControllerTrait, which in turn depends on the ContainerAwareTrait for the container injection. This however will pull additional services and methods that you won't need in a command so instead of injecting the whole container, the recommended approach is that you inject just the service you need, which in this case is the ObjectManager (ObjectManager is the common interface implemented both by the ORM and the ODM, if you use both or you just care about the ORM, you can use Doctrine\ORM\EntityManagerInterface instead).
<?php
// src/Command/MyCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\Common\Persistence\ObjectManager;
class MyCommand extends Command
{
// the name of the command (the part after "bin/console")
protected static $defaultName = 'app:mycommand';
private $manager;
public function __construct(ObjectManager $manager)
{
$this->manager = $manager;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// Now you have access to the manager methods in $this->manager
$repository = $this->manager->getRepository(/*...*/);
}
}
Related
I have created a simple test like this :
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\User;
use Illuminate\Support\Facades\Config;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class simpleTest extends TestCase
{
public $form_array = ['forms_data'];
/**
* A basic feature test example.
*
* #return void
*/
public function test_example()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
The test above runs without a problem .
I have a public property $form_array which in the example above has a static value, but when I'm trying to fill $form_array with __construct() in test, I get this error :
array_merge(): Argument #1 must be of type array, null given
at vendor/phpunit/phpunit/phpunit:98
94▕ unset($options);
95▕
96▕ require PHPUNIT_COMPOSER_INSTALL;
97▕
➜ 98▕ PHPUnit\TextUI\Command::main();
99▕
code :
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\User;
use Illuminate\Support\Facades\Config;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class simpleTest extends TestCase
{
public $form_array;
public function __construct(){
$this->form_array = ['forms_data'];
}
/**
* A basic feature test example.
*
* #return void
*/
public function test_example()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
My goal is fill the $form_data with values from config files in laravel which i found this way :
public function __construct(){
parent::setUp();
$this->form_array = Config::get('ravandro.form_array');
}
but even a simple $this->form_data = 'value' doesnt work , and im really confused !
btw , Im using Laravel Octane to serve my application but I dont think it has any effect to tests.
Php version : 8.1
Laravel : 9
php-unit: "phpunit/phpunit": "^9.3.3"
In tests the correct constructor method is called setUp(), which runs before every test on that class.
You can check an example of this on https://phpunit.readthedocs.io/en/9.5/fixtures.html#fixtures
I need to run the controller method every 2 hours. I read somewhere that you need to create a command and run this command by using CRON. It is correct?
MY COMMAND:
namespace AppBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Routing\Annotation\Route;
class RunCommand extends Command
{
// the name of the command (the part after "bin/console")
protected static $defaultName = 'app:run';
protected function configure()
{
// ...
}
protected function execute(InputInterface $input, OutputInterface $output)
{
echo 'BEGIN';
$controller = new \AppBundle\Controller\DefaultController();
$controller->storeAction();
echo 'END';
}
}
MY CONTROLLER:
/**
* #Route("/to-db", name="to-db")
*/
public function storeAction()
{
$entityManager = $this->getDoctrine()->getManager();
$data = new Skuska();
$data->setName('Keyboard');
$entityManager->persist($data);
$entityManager->flush();
// die();
}
My error: In ControllerTrait.php line 424: Call to a member function has() on null
Is my code correct? How do I run a method using cron?
I don't want to use another bundle. I want to program it myself
As mentioned in the comments, you should move the logic out of the controller and into a service, and use that service both in the command and in the controller.
With the default service autoloading configuration, you don't even have to care about your service declarations. Your command will automatically be a service, and you can inject other services into it.
https://symfony.com/doc/current/console/commands_as_services.html
For controllers, you don't even need to use a specific constructor.
https://symfony.com/doc/current/controller.html#fetching-services
<?php
// AppBundle/Service/StoreService.php
use AppBundle\Entity\Skuska;
use Doctrine\ORM\EntityManager;
class StoreService
{
/** #var EntityManager */
private $entityManager;
/**
* StoreService constructor.
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function store()
{
$data = new Skuska();
$data->setName('Keyboard');
$this->entityManager->persist($data);
$this->entityManager->flush();
}
}
<?php
// AppBundle/Controller/StoreController.php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use AppBundle\Service\StoreService;
class StoreController extends Controller
{
/**
* #Route("/to-db", name="to-db")
* #param StoreService $storeService
* #return Response
*/
// Hinting to you service like this should be enough for autoloading.
// No need for a specific constructor here.
public function storeAction(StoreService $storeService)
{
$storeService->store();
return new Response(
// Return something in you response.
);
}
}
<?php
// AppBundle/Command/RunCommand.php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use AppBundle\Service\StoreService;
class RunCommand extends Command
{
protected static $defaultName = 'app:run';
/** #var StoreService */
protected $storeService;
/**
* RunCommand constructor.
* #param StoreService $storeService
*/
public function __construct(StoreService $storeService)
{
$this->storeService = $storeService;
parent::__construct();
}
protected function configure()
{
// ...
}
protected function execute(InputInterface $input, OutputInterface $output)
{
echo 'BEGIN';
$this->storeService->store();
echo 'END';
}
}
I am using PhpStorm with Symfony. What I am trying to do is to debug a Symfony command from inside the IDE by using the debug button (Shift + F9).
I am getting the following error.
PHP Fatal error: Class 'Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand' not found in /home/user/Projects/project1/symfony/src/AppBundle/Command/testScriptCommand.php on line 8
PHP Stack trace:
It's weird as I have followed the Symfony documentation for creating commands and I have included the following classes:
<?php
namespace AppBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class testScriptCommand extends ContainerAwareCommand
{
protected function configure(): void
{
$this->setName('app:test-script');
}
protected function execute(InputInterface $input, OutputInterface $output): void
{
echo 1;
}
}
The debugger works inside the IDE until line 8 and once try to continue it fails with the already mentioned fatal error.
It seems to me as line 4 is not actually importing the ContainerAwareCommand that is needed.
Any ideas?
Extend Symfony\Component\Console\Command\Command
Dependency Inject the ContainerInterface with your commands constructor, something like this - in my case using autowired services:
/** #var ContainerInterface $container */
protected $container;
public function __construct(ContainerInterface $container)
{
parent::__construct();
$this->container = $container;
}
Then you should be able to call fe. $this->container->getParameter('project.parameter')
What documentation did you followed?
For creating Commands you need to extend Command, no ContainerAwareCommand
// src/Command/CreateUserCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CreateUserCommand extends Command
{
// the name of the command (the part after "bin/console")
protected static $defaultName = 'app:create-user';
protected function configure()
{
// ...
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// ...
}
}
For more information: https://symfony.com/doc/current/console.html
EDIT:
Adding info...
ContainerAwareCommand is for Symfony version <= 2.6 https://symfony.com/doc/2.6/cookbook/console/console_command.html Soooo old
I'm trying to extend my Laravel Artisan commands with a trait. The trait should capture all command line output and send it to Slack.
I've got the 'send messages to slack' part working with this package.
However I'm failing to capture the console output. This is what I've got:
namespace App\Traits;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;
trait NotifiesSlack
{
/**
* Execute the console command.
*
* #param \Symfony\Component\Console\Input\InputInterface $input
* #param \Symfony\Component\Console\Output\OutputInterface $output
* #return mixed
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$consoleOutput = new BufferedOutput;
$call = $this->laravel->call([$this, 'handle']);
$this->notifySlack($consoleOutput->fetch());
return $call;
}
public function notifySlack(string $output)
{
\Slack::send($output);
}
}
Am I overriding the right method? Are there other ways to capture the console output from the Command class?
Any help is welcome! Thanks in advance.
You are experiencing usual case of not able to override method via trait. It's obviously because the execute method is already declared in the class itself rendering the trait useless.
A quick and easy way, is to simply create your own abstract command class that extends the Illuminate\Console\Command; and override the execute method to your liking; afterwards use the abstract command class for your slack-reportable commands as base.
abstract class NotifiesSlackCommand extend Illuminate\Console\Command {
protected function execute(InputInterface $input, OutputInterface $output)
{
...
}
}
And real command that needs send to Slack
class ProcessImagesCommand extends NotifiesSlackCommand {
public function handle() {/* do magic */}
}
I'm trying to implement websockets in Symfony2,
I found this http://socketo.me/ which seems pretty good.
I try it out of Symfony and it works, this was just a simple call using telnet. But I don't know how to integrate this in Symfony.
I think I have to create a service but I don't know realy which kind of service and how to call it from the client
Thanks for your help.
First you should create a service. If you want to inject your entity manager and other dependencies, do it there.
In src/MyApp/MyBundle/Resources/config/services.yml:
services:
chat:
class: MyApp\MyBundle\Chat
arguments:
- #doctrine.orm.default_entity_manager
And in src/MyApp/MyBundle/Chat.php:
class Chat implements MessageComponentInterface {
/**
* #var \Doctrine\ORM\EntityManager
*/
protected $em;
/**
* Constructor
*
* #param \Doctrine\ORM\EntityManager $em
*/
public function __construct($em)
{
$this->em = $em;
}
// onOpen, onMessage, onClose, onError ...
Next, make a console command to run the server.
In src/MyApp/MyBundle/Command/ServerCommand.php
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Ratchet\Server\IoServer;
class ServerCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('chat:server')
->setDescription('Start the Chat server');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$chat = $this->getContainer()->get('chat');
$server = IoServer::factory($chat, 8080);
$server->run();
}
}
Now you have a Chat class with dependency injections, and you can run the server as a console command. Hope this helps!