error " Cannot access empty property" when I try a new command - symfony

I get this error
Cannot access empty property
when I try this command abc:check-car
<?php
namespace CarBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
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 AbcCheckCarCommand extends Command
{
protected $carChecker;
protected $manager;
/**
* AbcCheckCarCommand constructor.
* #param $carChecker
* #param $manager
*/
public function __construct($carChecker, $manager)
{
$this->carChecker = $carChecker;
$this->manager = $manager;
parent::__construct();
}
protected function configure()
{
$this
->setName('abc:check-car')
->setDescription('...')
->addArgument('format', InputArgument::OPTIONAL, 'format progress bar')
->addOption('option', null, InputOption::VALUE_NONE, 'Option description')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$carRepository = $this->$manager->getRepository('CarBundle:Car');
$cars = $carRepository->findAll();
$bar = new ProgressBar($output, count($cars));
$argument = $input->getArgument('format');
$bar->setFormat($argument);
$bar->start();
foreach ($cars as $car)
{
$this->carChecker->checkCar($car);
sleep(1);
$bar->advance();
}
$bar->finish();
}
}

change
$carRepository = $this->$manager->getRepository('CarBundle:Car');
to
$carRepository = $this->manager->getRepository('CarBundle:Car');
mind $ before manager

Related

command is not executed in a controller

I would like to run a command via a controller. My command run normaly in cli (tests are done)
My controller call the command and I got immediately
[OK] success
from the command return but tests did not run.
my controller
`
<?php
namespace App\Controller\Api;
use OpenApi\Annotations as OA;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Process\Process;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
class UnitTestController extends AbstractController
{
private $kernel;
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
}
#[Route('/unit_tests', name: 'unit_test', methods: 'GET')]
public function unitTest(): Response
{
$application = new Application($this->kernel);
$application->setAutoExit(false);
$input = new ArrayInput([
'command' => 'mapi:unitTests',
]);
$output = new BufferedOutput();
$application->run($input, $output);
// return the output, don't use if you used NullOutput()
$content = $output->fetch();
dd($content);
}
}
`
and my command
`
<?php
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
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 Symfony\Component\Process\Process;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
#[AsCommand(
name: 'mapi:unitTests',
description: 'lancement des tests unitaires',
)]
class unitTestCommand extends Command
{
/**
* #param InputInterface $input
* #param OutputInterface $output
* #return int
* #throws ClientExceptionInterface
* #throws DecodingExceptionInterface
* #throws RedirectionExceptionInterface
* #throws ServerExceptionInterface
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
ini_set('max_execution_time', 300);
$process = Process::fromShellCommandline('php vendor/bin/phpunit');
$process->run();
$io->success('success');
return Command::SUCCESS;
}
}
edit: no error, unit tests from command are not executed. when I run the command from the cli, everything is ok
`

Error Symfony when command execute

I create a symfony command that executes a function in a controller but when I run the command I get this error message:
[Symfony \ Component \ Debug \ Exception \ FatalThrowableError]
Fatal error: Call to a member function has () on null
My Services.yml
services:
app.site_batch:
class: App\SiteBundle\Controller\BatchController
arguments:
entityManager: "#doctrine.orm.entity_manager"
container: "#service_container"
My Controller
<?php
namespace App\SiteBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use ChrisCom\BDDBundle\Entity\Paiement;
use ChrisCom\BDDBundle\Entity\BBooking;
/**
* #Route("/batch")
*/
class BatchController extends Controller
{
private $log_file;
private $output;
private function openLog($filename) { $this->log_file = fopen($this->get('kernel')->getLogDir() . '/' . $filename, 'a'); }
private function log($line, $message)
{
if ($this->log_file)
{
$msg = sprintf("[%s %s:%03d] %s\n", date('d/m/Y H:i:s'), basename(__FILE__), $line, $message);
fputs($this->log_file, $msg);
$this->output .= $msg;
}
}
private function closeLog() { if ($this->log_file) fclose($this->log_file); }
/**
* #Route("/check_bookings", name="batch_check_bookings")
*/
public function checkBookingsAction($simul)
{
$em = $this->getDoctrine()->getManager();
$rep_book = $em->getRepository('ChrisComBDDBundle:BBooking');
$cur_date = new \DateTime();
$format_cmp = 'YmdHis';
$flush = false;
....
My Command
<?php
namespace App\SiteBundle\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 CheckBookingsCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('cron:checkBookings')
->setDescription('Vérification des réservation')
->addOption(
'simul',
null,
InputOption::VALUE_NONE,
'Si défini, le programme ne fera pas de COMMIT en base de donnée'
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln("Starting the cron");
// Call the method here
$version = $this->getContainer()->get('app.bdd')->parameterGet('VERSION');
$simul = ($input->getOption('simul'));
if ($simul) $output->writeln("Mode simulation");
$output->writeln($this->getContainer()->get('app.site_batch')->checkBookingsAction($simul));
$output->writeln("Cron completed");
}
}
I searched but without result ...
Thank you for help.
In your BatchController :
public function checkBookingsAction($simul)
{
$em = $this->container->get('doctrine')->getManager();
// ...
}
When you use a controller as a service, you lose the possibility to use $this->getDoctrine().

Symfony mock service when test console command

How to mock some service when test console command. I have some console command, in this command I get some service and I want mock this service
console command
const APP_SATISFACTION_REPORT = 'app:satisfactionrepor';
protected function configure()
{
$this
->setName(self::APP_SATISFACTION_REPORT)
->setDescription('Send Satisfaction Report');
}
/**
* #param InputInterface $input
* #param OutputInterface $output
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$container = $this->getContainer();
$serviceCompanyRepo = $container->get('app.repository.orm.service_company_repository');
$satisfactionReport = $container->get('app.services.satisfaction_report');
/** #var ServiceCompany $serviceCompany */
foreach ($serviceCompanyRepo->findAll() as $serviceCompany) {
try {
$satisfactionReport->sendReport($serviceCompany);
} catch (\Exception $e) {
$io->warning(sprintf(
'Failed to send satisfaction report for service company with ID %s',
$serviceCompany->getId()
));
}
}
}
and my tests
/** #var Console\Application $application */
protected $application;
protected $container;
/** #var BufferedOutput $output */
protected $output;
/**
* #var ServiceCompanyRepository
*/
private $serviceCompanyRepository;
prepare console command
public function setUp()
{
parent::setUp();
$entityManager = $this->getEntityManager();
$this->serviceCompanyRepository = $entityManager->getRepository(ServiceCompany::class);
static::bootKernel();
$this->container = static::$kernel->getContainer();
$this->application = new Console\Application(static::$kernel);
$this->application->setAutoExit(false);
$master = new SatisfactionReportCommand();
$this->application->add($master);
}
public function setUpMaster() {
$this->output = new BufferedOutput();
$this->application->run(new ArgvInput([
'./bin/console',
SatisfactionReportCommand::APP_SATISFACTION_REPORT,
]), $this->output);
}
public function testGetMasterOutput()
{
$this->loadFixture(ServiceCompany::class);
/** #var ServiceCompany[] $serviceCompanies */
$serviceCompanies = $this->serviceCompanyRepository->findAll();
$this->assertCount(2, $serviceCompanies);
$client = self::createClient();
mock service app.services.satisfaction_report
$service = $this->getMockService($serviceCompanies);
and set it in container
$client->getContainer()->set('app.services.satisfaction_report', $service);
$this->setUpMaster();
$output = $this->output->fetch();
}
protected function getMockService($serviceCompanies)
{
$service = $this->getMockBuilder(SatisfactionReport::class)
->setMethods(['sendReport'])
->disableOriginalConstructor()
->getMock();
$service
->expects($this->exactly(2))
->method('sendReport')
->withConsecutive(
[$serviceCompanies[0]],
[$serviceCompanies[1]]
);
return $service;
}
How to mock app.services.satisfaction_report? Set in container app.services.satisfaction_report not help me
I had same problem but I resolved it.
I have base class:
class TestCase extends WebTestCase
{
/** #var Application */
private $application;
private $mailServiceMock;
protected function setMailService(MailService $mailServiceMock): void
{
$this->mailServiceMock = $mailServiceMock;
}
protected function getApplication(): Application
{
static::bootKernel();
static::$kernel->getContainer()->get('test.client');
$this->setMocks();
$this->application = new Application(static::$kernel);
$this->application->setCatchExceptions(false);
$this->application->setAutoExit(false);
return $this->application;
}
protected function execute(string $action, array $arguments = [], array $inputs = []): CommandTester
{
$tester = (new CommandTester($this->getApplication()->find($action)))->setInputs($inputs);
$tester->execute($arguments);
return $tester;
}
private function setMocks(): void
{
if ($this->mailServiceMock) {
static::$kernel->getContainer()->set('mail', $this->mailServiceMock);
}
}
}
And test class
class SendEmailCommandTest extends TestCase
{
public function testExecuteSendingError(): void
{
$mailServiceMock = $this->getMockBuilder(MailService::class)->disableOriginalConstructor()
->setMethods(['sendEmail'])->getMock();
$mailServiceMock->method('sendEmail')->willThrowException(new \Exception());
$this->setMailService($mailServiceMock);
$tester = $this->execute(SendEmailCommand::COMMAND_NAME, self::NORMAL_PAYLOAD);
$this->assertEquals(SendEmailCommand::STATUS_CODE_EMAIL_SENDING_ERROR, $tester->getStatusCode());
}
}
As you can see I set mail service right after booting kernel.
And here you can see my services.yaml:
services:
mail.command.send.email:
autowire: true
class: App\Command\SendEmailCommand
arguments: ["#logger", "#mail"]
tags:
- {name: console.command, command: "mail:send.email"}
If you create the commands as a service, where the framework injects the services automatically (either autowired, or with an explicit argument list) into a constructor (tip: in the command, call the parent::__construct()), then the test can create whatever mock or other replacement service that matches the parameter typehint (or interface).
here is my example class:
class MainCommandTest extends IntegrationTestCase
{
/**
* #var MainCommand
*/
protected $subject;
/**
* #var Application
*/
protected $application;
/**
* sets test subject
*
* #return void
*/
public function setUp()
{
parent::setUp();
static::bootKernel();
$readStreams = new ReadStreams();
$udpStreamMock = $this->getMockBuilder(UdpStream::class)->disableOriginalConstructor()->setMethods(['readIncomingStreams'])->getMock();
$udpStreamMock->expects($this->once())->method('readIncomingStreams')->willReturn($readStreams);
static::$kernel->getContainer()->set(UdpStream::class, $udpStreamMock);
$application = new Application($this::$kernel);
$this->subject = $this->getService(MainCommand::class);
$application->add( $this->subject);
$this->application = $application;
}
/**
* Tests command in $subject command,
*
* #return void
*/
public function testCommand()
{
$command = $this->application->find( $this->subject->getName());
$commandTester = new CommandTester($command);
$commandTester->execute(
[
'command' => $this->subject->getName()
]
);
$this->stringContains($commandTester->getDisplay(true), 'finished');
$this->assertEquals($commandTester->getStatusCode(), 0);
}
}

How I can generate the slug field the from existing data in database - Doctrine Symfony2

This is my entity, and i used a gedmo annotation, when a new register is created (persits) the slug works correctly but how can i auto-generate slug texts from existing database
/**
* #Gedmo\Slug(fields={"name"})
* #ORM\Column(type="string", unique=true)
*/
protected $slug;
You have to do it manually by selecting all the value without a slug and setting the slug value to null as describe inside the documentation of the Sluggable Behavior.
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/sluggable.md#regenerating-slug
Here is a naive Symfony command to regenerate all slugs of the given classes:
<?php
namespace App\Command;
use App\Entity\Foo;
use App\Entity\Bar;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class RegenerateSlugs extends Command
{
private $doctrine;
protected static $defaultName = "app:regenerate-slugs";
public function __construct(ManagerRegistry $doctrine)
{
parent::__construct();
$this->doctrine = $doctrine;
}
protected function configure(): void
{
$this
->setDescription('Regenerate the slugs for all Foo and Bar entities.')
;
}
protected function execute(InputInterface $input, OutputInterface $output): void
{
$manager = $this->doctrine->getManager();
// Change the next line by your classes
foreach ([Foo::class, Bar::class] as $class) {
foreach ($manager->getRepository($class)->findAll() as $entity) {
$entity->setSlug(null);
//$entity->slug = null; // If you use public properties
}
$manager->flush();
$manager->clear();
$output->writeln("Slugs of \"$class\" updated.");
}
}
}
Hi hope it may help someone stumbling upon this question!

RenderView in Symfony Command usage

How can I use $this->renderView inside a symfony Command (not inside a controller)? I new about the function "renderView" but what do I have to setup to use it wihtin a command?
Thank you in advance an regards
Your command class must extends the ContainerAwareCommand abstract class and then you can do:
$this->getContainer()->get('templating')->render($view, $parameters);
When it comes to commands that extend ContainerAwareCommand the proper way to obtain the container is by getContainer() unlike in controller shortcut.
In Symfony 4 I could not get $this->getContainer()->get('templating')->render($view, $parameters); to work.
I set the namespace use for Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand and extended ContainerAwareCommand class EmailCommand extends ContainerAwareCommand
I get an exception thrown
[Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException]
You have requested a non-existent service "templating".
For Symfony 4, this is the solution I came up with.
First I installed Twig.
composer require twig
Then created my own twig service.
<?php
# src/Service/Twig.php
namespace App\Service;
use Symfony\Component\HttpKernel\KernelInterface;
class Twig extends \Twig_Environment {
public function __construct(KernelInterface $kernel) {
$loader = new \Twig_Loader_Filesystem($kernel->getProjectDir());
parent::__construct($loader);
}
}
Now my email command looks like this.
<?php
# src/Command/EmailCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command,
Symfony\Component\Console\Input\InputInterface,
Symfony\Component\Console\Output\OutputInterface,
App\Service\Twig;
class EmailCommand extends Command {
protected static $defaultName = 'mybot:email';
private $mailer,
$twig;
public function __construct(\Swift_Mailer $mailer, Twig $twig) {
$this->mailer = $mailer;
$this->twig = $twig;
parent::__construct();
}
protected function configure() {
$this->setDescription('Email bot.');
}
protected function execute(InputInterface $input, OutputInterface $output) {
$template = $this->twig->load('templates/email.html.twig');
$message = (new \Swift_Message('Hello Email'))
->setFrom('emailbot#domain.com')
->setTo('someone#somewhere.com')
->setBody(
$template->render(['name' => 'Fabien']),
'text/html'
);
$this->mailer->send($message);
}
}
Yet another one: rely on dependency injection, i.e. inject ContainerInterface
namespace AppBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Psr\Container\ContainerInterface;
class SampleCommand extends Command
{
public function __construct(ContainerInterface $container)
{
$this->templating = $container->get('templating');
parent::__construct();
}
protected function configure()
{
$this->setName('app:my-command')
->setDescription('Do my command using render');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$data = retrieveSomeData();
$csv = $this->templating->render('path/to/sample.csv.twig',
array('data' => $data));
$output->write($csv);
}
private $templating;
}
This relies on Symfony to inject the container, which in turn is used to retrieve either templating or twig or whatever you need for your custom command.

Resources