Symfony 3.4 [Symfony\Component\Console\Exception\CommandNotFoundException] Command - console

(The text you have in front of you comes from an online translator).
Context: Develop a Symfony command to retrieve in database the addresses of my localization table that do not have GPS coordinates, (the localizations whose fields latitude and longitude are null). Find the GPS coordinates corresponding to the address via Google API (google geocoding) and then insert the coordinates in database.
Code files :
LocationCommand.php
<?php
namespace Keosu\DataModel\LocationModelBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Keosu\DataModel\LocationModelBundle\Utils\LocationManager;
class LocationCommand extends ContainerAwareCommand
{
private $locationManager;
public function __construct(LocationManager $locationManager)
{
parent::__construct();
$this->locationManager = $locationManager;
}
protected function configure()
{
$this
->setName('keosu:coordinated')
->setDescription('Populate table location with google coordinated , if the field long and lat are empty')
->setHelp('For call this command enter app/console Keosu:coordinated ')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$em = $this->getDoctrine()->getManager();
$apiUrl = "http://maps.googleapis.com/maps/api/geocode/json?";
$apiKey = $this->getContainer()->getParameter('google_api_key');
//My collection of location
$locations = $this->locationManager->findAll();
foreach ($locations as $location)
{
if ($location->getLat() == null && $location->getLng() == null)
{
$address = $location->getAddress();
$address .= ',' . $location->getPostalCode();
$urlAddress = preg_replace('/\s+/', '+', $address);
$geolocUrl = $apiUrl.'address='.$urlAddress.'&key='.$apiKey;
//Query to send for Geocoding API
$query = sprintf($geolocUrl,urlencode(utf8_encode($address)));
$result = json_decode(file_get_contents($query));
$json = $result->results[0];
$address->setLat($json->geometry->location->lat);
$address->setLng($json->geometry->location->lng);
$em->persist($address);
$em->flush();
}
}
}
}
The service I use for my command : LocationManager.php
<?php
namespace Keosu\DataModel\LocationModelBundle\Utils;
use Doctrine\ORM\EntityManager;
class LocationManager
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function findAll()
{
//Find all location
return $this->em->getRepository('LocationModelBundle:Location')->findAll();
}
}
The problem: When in the console I enter "app/console keosu:coordinated" to start my command, the console displays this error:
[Symfony\Component\Console\Exception\CommandNotFoundException]
Command "keosu:coordinated" is not defined.
Did you mean this?
keosu:export
keosu: export is a command that is in another directory. The command is in this directory (Keosu/CoreBundle/Command/ExportCommand.php).
I did check the namespace yet the error persists. I do not even know if my code will process my request, since the command does not start.
Here is the architecture of the application.

Related

How to use UserPasswordHasher in Symfony command?

protected function execute(InputInterface $input, OutputInterface $output, UserPasswordHasherInterface $passwordHasher): int
{
$io = new SymfonyStyle($input, $output);
$arg_email = $input->getArgument('email');
$arg_password = $input->getArgument('password');
if ($arg_email || $arg_password) {
$io->note(sprintf('You used the email adress: %s', $arg_email));
$io->note(sprintf('You used the password: %s', $arg_password));
$user = New User();
dd($passwordHasher->hashPassword(
$user,
'test'
));
$user->setEmail($arg_email);
$user->setPassword($passwordHasher->hashPassword(
$user,
'test'
));
$user->setActive(1);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($user);
$em->flush();
$io->success('User has been created!');
}
return Command::SUCCESS;
}
So I'm trying to build a custom command that will get your arg. (email/pass) and hash the password and create a user. Simple enough I thought.. I'm not quite sure whats going wrong because I'm getting weird compilere errors, I've tried multiple solutions provided by the internet but it comes all back to some what the same error.
"type" => 64 "message" => "Declaration of
App\Command\CreateUserCommand::execute(Symfony\Component\Console\Input\InputInterface
$input, Symfony\Component\Console\Output\OutputInterface $output,
App\Command\UserPasswordHasherInterface $passwordHasher): int must be
compatible with
Symfony\Component\Console\Command\Command::execute(Symfony\Component\Console\Input\InputInterface
$input, Symfony\Component\Console\Output\OutputInterface $output)"
"file" =>
"E:\BackYardBBQ\project\src\Command\CreateUserCommand.php"
Some of the things i tried are:
putting the interface inside the __constructor for auto wiring = getting an error that you cant use auto wiring
building it inside an controller or the authenticator = not working for the same reason my other failed attempts where.
building it inside the constructor of the controller
The error if you try autowiring
Cannot autowire service "App\Command\CreateUserCommand": argument
"$entityManager" of method "__construct()" has type
"App\Command\EntityManagerInterface" but this class was not found.
Make sure you enable namespaces
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use App\Entity\User;
You also need to add EntityManagerInterface and UserPasswordHasherInterface to your team constructor
private EntityManagerInterface $entityManager;
private UserPasswordHasherInterface $passwordHasher;
public function __construct(EntityManagerInterface $em, UserPasswordHasherInterface $passwordHasherInterface){
$this->entityManager = $em;
$this->passwordHasher = $passwordHasherInterface;
parent::__construct();
}
Now you will be able to use them in your command like this:
$user->setPassword($this->passwordHasher->hashPassword($user, 'test'));
$this->entityManager->persist($user);
$this->entityManager->flush();

Obtaining output of a symfony command via shell_exec / ApplicationTester etc. in symfony unit test

I'd like to run a command in a separate process from a unit test and obtain its output. I've tested several options like using shell_exec or the ApplicationTester-class. While the command is obviously executed (I checked by inserting some side-effects), its output cannot be obtained when executed from the unit test.
However, if I create a small stand-alone php-file (see below, runDummyCmd.php) that contains a corresponding shell_exec, I obtain the output of the symfony command.
How to reproduce
Dummy-Command:
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DummyCommand extends Command
{
/**
* {#inheritdoc}
*/
protected function configure()
{
$this
->setName('app:dummycmd')
->setDescription('Can be used for testing commands')
->setHelp('Does a simple output.');
}
/**
* {#inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('Test!');
return 0;
}
}
Test-Case:
class MyTest extends MyKernelTestCase
{
protected $projectDir;
protected function setUp(): void
{
parent::setUp();
$this->projectDir = static::$container->getParameter('kernel.project_dir');
}
public function testGetOutputOfCommand() {
$cmd = 'env APP_ENV=test '. $this->projectDir . '/bin/console app:dummycmd';
dump($cmd);
$output = shell_exec($cmd);
dump($output);
}
}
runDummyCmd.php:
<?php
$cmd = 'env APP_ENV=test bin/console app:dummycmd';
var_dump($cmd);
$output = shell_exec($cmd);
var_dump($output);
?>
Outputs
Output of the unit-test:
"env APP_ENV=test PROJECT_DIR/bin/console app:dummycmd"
null
Output of the execution of runDummyCmd.php on the console:
string(41) "env APP_ENV=test bin/console app:dummycmd"
string(6) "Test!
"
Have you tried the CommandTester class?
$command = $application->find('app:dummycmd');
$commandTester = new CommandTester($command);
$commandTester->execute([
'command' => $command->getName()
]);
$output = $commandTester->getDisplay();

Testing fixtures with addReference in Symfony 4

I'm working on a Symfony's project and i have some issues while testing with phpunit.
I have StatusFixtures with addReference to be used in BriefFixtures and this work correctly when i do doctrine:fixtures:load (with correct dependency to load Status before Brief).
But, when i run my tests, using those fixtures, the following error is coming : Error: Call to a member function addReference() on null
My StatusFixtures.php
<?php
namespace App\DataFixtures;
use App\Entity\Status;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
class StatusFixtures extends Fixture
{
const Status_Reference = 'status';
public function load(ObjectManager $manager)
{
// some code to assign values
$manager->persist($activeStatus);
$this->addReference(self::Status_Reference, $activeStatus);
$manager->flush();
}
}
My BriefFixtures.php
<?php
namespace App\DataFixtures;
use App\Entity\Brief;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
class BriefFixtures extends Fixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager)
{
// some code to assign values
$briefValid->setStatus($this->getReference(StatusFixtures::Status_Reference));
$manager->persist($briefValid);
$manager->flush();
}
public function getDependencies()
{
return array(
StatusFixtures::class,
);
}
}
And i'm loading fixtures this way in my tests
private $entityManager;
protected function setUp()
{
$kernel = self::bootKernel();
$this->entityManager = $kernel->getContainer()
->get('doctrine')
->getManager();
$status = new StatusFixtures();
$status->load($this->entityManager);
$fixture = new BriefFixtures();
$fixture->load($this->entityManager);
}
And my error Error: Call to a member function addReference() on null
$this from StatusFixtures seems to be null, but i don't understand why this correctly works when i'm loading fixtures and no more when i'm running my tests.
Maybe something is missing in setUp() ?
Thank you for your help
The problem lies in the Symfony documentation for Fixtures. It makes you feel like
$fixture->load($this->entityManager); will simply load the fixture, but that is not true. It is simple when you use the command doctrine:fixtures:load because it does more than just above load function call.
Going with third party solutions will be the quickest and probably the best solution. Here are few libraries that you can use:
liip/LiipTestFixturesBundle
hautelook/AliceBundle (thanks michal)
The error you are getting comes from ReferenceRepository object that is supposed to store the references, but it is null. Who actually sets up this repository, it is Doctrine\Common\DataFixtures\Executor\AbstractExecutor. What you need is a Loader that loads the fixture by creating everything needed for it to work. One of those loaders is Doctrine\Bundle\FixturesBundle\Loader\SymfonyFixturesLoader that your command doctrine:fixtures:load is using. You can use that loader or write your own loader. You can see that what this loader has to do to provide you the expected results. But that still is not it, you also need Doctrine\Common\DataFixtures\Executor\ORMExecutor because your Fixture is a database entity and you need to persist it. You can see that
how doctrine:fixtures:load makes use of SymfonyFixturesLoader and ORMExecutor to provide you expected result. So, you will have to write your own solution for this. I worte a Loader for myself before because I didn't want to go with third party solutions. I am putting it below. It may not serve your purpose exactly, but it will give you ideas how to write your own Loader if you want to.
namespace App\Tests\Extra;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\ORM\EntityManagerInterface;
use App\Tests\Extra\Exception\FixtureNotLoadedException;
class FixtureLoader
{
private $entityManager;
private $loader;
private $registry;
public function __construct(
EntityManagerInterface $entityManager,
ManagerRegistry $registry
) {
$this->entityManager = $entityManager;
$this->registry = $registry;
}
public function loadFixtures(array $classNames) : void
{
$this->loader = new Loader();
foreach ($classNames as $className) {
$this->loader->addFixture(new $className());
}
$executor = new ORMExecutor($this->entityManager, new ORMPurger());
$executor->execute($this->loader->getFixtures());
}
public function getFixture(string $className) : Fixture
{
if ($this->loader == null) {
throw new FixtureNotLoadedException(
sprintf(
'The fixture %s must be loaded before you can access it.',
$className
)
);
}
return $this->loader->getFixture($className);
}
private function getPurger() : ORMPurger
{
$purger = new ORMPurger($this->entityManager);
$purger->setPurgeMode(ORMPurger::PURGE_MODE_TRUNCATE);
return $purger;
}
public function cleanDatabase() : void
{
$connection = $this->entityManager->getConnection();
$mysql = ('ORM' === $this->registry->getName()
&& $connection->getDatabasePlatform() instanceof MySqlPlatform);
if ($mysql) {
$connection->query('SET FOREIGN_KEY_CHECKS=0');
}
$this->getPurger()->purge();
if ($mysql) {
$connection->query('SET FOREIGN_KEY_CHECKS=1');
}
}
}

Service Uploader Missing Argument Exception

I created an upload service to use it in different controllers in my code.
But i get an exception now like this when i want to access to the page for add a new gig :
Controller
"Booking\RoosterBundle\Controller\ArtistController::addGigAction()"
requires that you provide a value for the "$fileUploader" argument.
Why i'm getting this ? the value should be $file but i need to access to my form first and submit the file ... hope someone could help me. thanks a lot in advance
here is my service :
<?php
namespace Booking\RoosterBundle\Uploader;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileUploader
{
private $targetDir;
public function __construct($targetDir)
{
$this->targetDir = $targetDir;
}
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->getTargetDir(), $fileName);
return $fileName;
}
public function getTargetDir()
{
return $this->targetDir;
}
}
Here my controller :
public function addGigAction(Request $request , $id , FileUploader $fileUploader ){
...
$gig = new Gig();
$form = $this->createForm(GigType::class, $gig);
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
$file = $gig->getFlyer();
$fileName = $fileUploader->upload($file);
$gig->setFlyer($fileName);
$em->persist($gig);
$em->flush();
...
}
And here my service declaration in my bundle
booking_rooster.uploader:
class: Booking\RoosterBundle\Uploader\FileUploader
arguments:
$targetDir: '%upload_directory%'
This is not how you call a service !
Don't pass it as argument , call it like this :
$container->get('booking_rooster.uploader');

Symfony2 Unique Validation for multiple fields

I have two fields and want a unique validation on both of them combined.
Meaning name and city combination should be unique. But validation is triggering only for name
Entity\Location:
constraints:
- Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity:
fields: [name, city]
message: "Location for given City Already Exists"
You will have to write a call back validation to accomplish this.The call back method will check whether there are any existing locations for the given combination of city and name and, if there exist any location, it will throw a form error. In this example. you will have to call the entity manager within the entity. So, the service container is passed along with the bundle and is then called.
In security.yml
Venom\ExampleBundle\Entity\Location:
constraints:
- Callback:
methods: [isUniqueCityAndNameCombination]
In entity
use Symfony\Component\Validator\ExecutionContext;
use Venom\ExampleBundle;
public function isUniqueCityAndNameCombination(ExecutionContext $context)
{
$city = $this->getCity();
$name = $this->getName();
//you will have to call the Entity repository within the entity
$em = ExampleBundle::getContainer()->get('doctrine')->getEntityManager();
$location = $em->getRepository('ExampleBundle:Location')->findByNameAndCity(
$city, $name);
if($location) {
$propertyPath = $context->getPropertyPath() . '.name';
$context->setPropertyPath($propertyPath);
$context->addViolation("Location for given City Already Exists", array(), null);
}
return;
}
In the repository
public function dindByNameAndCity($city, $name)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$em = $this->getEntityManager();
$qb->select('a')
->from('ExampleBundle:Location', 'a')
->andWhere('a.city = :city')
->andWhere('a.name = :name')
->setParameter('city', $city)
->setParameter('name', $name)
;
$q = $qb->getQuery();
$entity = $q->getOneOrNullResult();
return $entity;
}
In the bundle file, in this case ExampleBundle.php
namespace Venom\ExampleBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use \Symfony\Component\DependencyInjection\ContainerInterface;
class ExampleBundle extends Bundle
{
private static $containerInstance = null;
public function setContainer(ContainerInterface $container = null)
{
parent::setContainer($container);
self::$containerInstance = $container;
}
public static function getContainer()
{
return self::$containerInstance;
}
}

Resources