Call to a member function encodePassword() on null, Symfony 4 - symfony

I'm trying to save an encoded password to my entity by using the UserPasswordEncoderInterface class.
class UserFixture extends Fixture
{
private $encoder;
public function _construct( UserPasswordEncoderInterface $encoder){
$this->encoder = $encoder;
}
public function load(ObjectManager $manager)
{
$user = new User();
$user->setUsername('admin');
$user->setPassword(
$this->encoder->encodePassword($user, '0000')
);
$user->setEmail('no-reply#hotmail.com');
$manager->persist($user);
$manager->flush();
}
}
But I'm getting the follwing error: Call to a member function encodePassword() on null. Can't find what I'm doing wrong here!

You should first check your namespace, but as #Cerad says, you mistyped __construct() which is the reason why your encoder is null.
The right command to use in order to load your fixture properly might be :
php bin/console doctrine:fixtures:load
As the documentation recommends : https://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html

Related

Symfony User entity (PhpStorm warning)

I have Symfony entity with auto-generated setUser() method:
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
In controller I use built-in getUser() method, which returns UserInterface object. And when I pass that UserInterface object to setUser() method, PhpStorm complains that:
Expected parameter of type '\App\Entity\User', 'object|\Symfony\Component\Security\Core\User\UserInterface' provided
I'd like to write code without such PhpStorm warnings. Should I create new User() to pass it to setUser() method or just ignore that?
Solution: (thanks to Jakumi comment)
public function __invoke() {
/** #var $user App\Entity\User */
$user = $this->getUser();
$entity = new Entity();
$entity->setUser($user);
//...
}

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');
}
}
}

Symfony 4 AbstractController Issue with Parameter Count

I'm trying to write an API in Symfony 4. I've hit a problem with my controller methods when trying to use DependencyInjection for a service API class I created. I've tried several different ways to write the code and can not figure it out.
https://symfony.com/doc/current/components/dependency_injection.html
I can make a getNext() (instead of get() below) method and the code will function as expected, but if I try to use a get() method I will get an error. These are the basic classes involved. Most of the code has been removed.
class AppointmentController extends AbstractController
{
/**
* #Route("/appointment/getNext", name="appointment/getNext")
*
*/
public function get(string $id = null, CernerFhir $fhirApi)
{
$request = Request::createFromGlobals();
...more code...
}
}
class CernerFhir
{
public function __construct(LoggerInterface $logger, ParameterBagInterface $params)
{
$this->logger = $logger;
$this->params = $params;
}
}
}
Warning: Declaration of App\Controller\AppointmentController::get(?string $id, App\Service\CernerFhir $fhirApi) should be compatible with Symfony\Bundle\FrameworkBundle\Controller\AbstractController::get(string $id)
AbstractController uses an interface that defines a get() method with a specific number of parameter and return type. If you wan't to overwrite it's get method (which i do no recommend), you have to write it so that it's compatible with it's definition in the interface.
http://php.net/manual/en/language.oop5.interfaces.php

Dependency Injection with Fixtures

I am building a web application using Symfony 4. I am trying to load fixtures from my functional tests. I have created a fixture class:
<?php
namespace App\DataFixtures;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class TestFixtures extends Fixture
{
private $encoder;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder = $encoder;
}
public function load(ObjectManager $em)
{
$user = new User();
$user->setUsername('user#gamil.com');
$user->setIsActive(true);
$user->setEmail('user#gmail.com');
$user->setFirstName('John');
$user->setLastName('Doe');
$user->setSchool($school);
$user->setRoles(['ROLE_USER']);
$password = $this->encoder->encodePassword($user, 'pass1234');
$user->setPassword($password);
$em->persist($user);
$em->flush();
return $user;
}
}
I have configured services_test.yaml to pass the service to the fixture:
services:
_defaults:
public: true
App\DataFixtures\TestFixtures:
arguments: ['#security.password_encoder']
However, when I try to run my tests I get the following error:
ArgumentCountError: Too few arguments to function App\DataFixtures\TestFixtures::__construct(), 0 passed in /Development/app/src/Test/ApiTestCase.php on line 38 and exactly 1 expected
This is the function from my test case:
protected function setUp()
{
$this->client = self::$staticClient;
$this->purgeDatabase();
$em = $this->getEntityManager();
$fixture = new TestFixtures();
$fixture->load($em);
}
Thanks in advance for any advice offered on this...
You are instantiating a class:
$fixture = new TestFixtures(); whereas you need it as a service.
You need to either, do this $fixture = new TestFixtures(encoder);
where encoder is either an instantiated class or a mock of your encoder class, or, retrieve TestFixtures class from the client's container. I'm not entirely sure but try
$fixture = $this->client->getContainer()->get('App\DataFixtures\TestFixtures');

How to load Symfony2 fixtures from migration class?

We've built up a set of data fixtures to seed the database with all our reference values. We are also using the DoctrineMigrationsBundle to manage schema updates. We would like to trigger the fixture load within our initial schema migration class so the system gets populated before running any additional schema updates.
I found in the docs that you can make migration classes container aware, but I can't figure out how to jump from that to calling/running the data fixtures. I haven't found any good answers on Stackoverflow or via google. Has anyone done this and can point me in the right direction? (or have suggestions on a better way to manage seed data in conjunction with schema migrations). Thanks.
This is using Symfony Version: 2.4
This is interesting question. I've found the "dirty" solution, but it works well.
namespace Application\Migrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
class Version20140811164659 extends AbstractMigration implements ContainerAwareInterface
{
private $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function up(Schema $schema)
{
// ... your code here
}
public function postUp(Schema $schema)
{
// here you have to define fixtures dir
$this->loadFixtures('src/Acme/BlogBundle/DataFixtures/ORM');
}
public function down(Schema $schema)
{
// ... your code here
}
public function loadFixtures($dir, $append = true)
{
$kernel = $this->container->get('kernel');
$application = new \Symfony\Bundle\FrameworkBundle\Console\Application($kernel);
$application->setAutoExit(false);
//Loading Fixtures
$options = array('command' => 'doctrine:fixtures:load', "--fixtures" => $dir, "--append" => (boolean) $append);
$application->run(new \Symfony\Component\Console\Input\ArrayInput($options));
}
}
This solution simply running console command php app/console doctrine:fixtures:load --fixtures=src/Acme/BlogBundle/DataFixtures/ORM --append after "up" migration.
Sorry for poore English. If you'll find clear solution, share it ;)
I've made a migration class to address this very problem. The code is essentially inspired from the doctrine:fixtures:load command.
<?php
namespace AppBundle\Migrations;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class AbstractFixturesAwareMigration extends AbstractMigration implements ContainerAwareInterface
{
private $container;
private $fixtures;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
protected function getContainer()
{
return $this->container;
}
protected function addFixture(FixtureInterface $fixture)
{
if(null === $this->fixtures) {
$this->fixtures = new ContainerAwareLoader($this->getContainer());
}
$this->fixtures->addFixture($fixture);
return $this;
}
protected function executeFixtures($em = null, $append = true, $purgeMode = ORMPurger::PURGE_MODE_DELETE)
{
$em = $this->getContainer()->get('doctrine')->getManager($em);
$purger = new ORMPurger($em);
$purger->setPurgeMode($purgeMode);
$executor = new ORMExecutor($em, $purger);
$executor->execute($this->fixtures->getFixtures(), $append);
$this->fixtures = null;
return $this;
}
}
Usage is pretty straightforward:
<?php
namespace Application\Migrations;
use AppBundle\Migrations\AbstractFixturesAwareMigration
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20170726102103 extends AbstractFixturesAwareMigration
{
/**
* #param Schema $schema
*/
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
// [...]
}
public function postUp(Schema $schema)
{
// LoadMyData can be any fixture class
$this->addFixture(new LoadMyData());
$this->executeFixtures();
}
/**
* #param Schema $schema
*/
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
// [...]
}
}

Resources