Symfony 5 tests phpunit Doctrine problem update database row - php

I have the following problem with doctrine when testing a symfony 5 application. Instead of updating the rows in the database, new rows are created when the persist () method is called or when cascade = {"persist"} is defined in Entity. The above issue only occurs in a test environment. Everything works fine in the app itself.
sample test code (maximally simplified to show the problem)
class GetReadyArticlesTest extends FunctionalDBTest
{
protected function setUp():void
{
parent::setUp();
$this->addFixture(new ConfigurationFixtures());
$this->addFixture(new CopyWriterTextOrderFixtures());
$this->executeFixtures();
}
protected function tearDown(): void
{
parent::tearDown();
}
public function testProcessSaveArticles()
{
$textOrderRepository = $this->entityManager->getRepository(CopywriterTextOrder::class);
$textOrderEntity = $textOrderRepository->find(1);
$textOrderEntity->setCharacters(4500);
$this->entityManager->persist($textOrderEntity);
$this->entityManager->flush();
}
}
Abstract class FunctionalDBTest:
abstract class FunctionalDBTest extends FunctionalTest
{
/**
* #var EntityManagerInterface
*/
protected $entityManager;
/**
* #var ORMExecutor
*/
private $fixtureExecutor;
/**
* #var ContainerAwareLoader
*/
private $fixtureLoader;
protected function setUp(): void
{
parent::setUp();
if ($this->getContainer()->getParameter('kernel.environment') !== 'test') {
die('Wymagane środowisko testowe');
}
$this->entityManager = $this
->getContainer()
->get('doctrine')
->getManager();
$schemaTool = new SchemaTool($this->entityManager);
$schemaTool->updateSchema($this->entityManager->getMetadataFactory()->getAllMetadata());
}
protected function addFixture(FixtureInterface $fixture): void
{
$this->getFixtureLoader()->addFixture($fixture);
}
protected function executeFixtures(): void
{
$this->getFixtureExecutor()->execute($this->getFixtureLoader()->getFixtures());
}
private function getFixtureExecutor(): ORMExecutor
{
if (!$this->fixtureExecutor) {
$this->fixtureExecutor = new ORMExecutor($this->entityManager, new ORMPurger($this->entityManager));
}
return $this->fixtureExecutor;
}
private function getFixtureLoader(): ContainerAwareLoader
{
if (!$this->fixtureLoader) {
$this->fixtureLoader = new ContainerAwareLoader($this->getContainer());
}
return $this->fixtureLoader;
}
protected function tearDown(): void
{
(new SchemaTool($this->entityManager))->dropDatabase();
parent::tearDown();
$this->entityManager->close();
$this->entityManager = null;
}
}
Removing the persist () method in this example fixes the problem. But in case I want to save a new relation to the table, it also generates a new main object. the problem only occurs in tests.

Related

Doctrine ManyToMany always returns an empty collection

I have these two classes with ManyToMany association:
One class:
/**
* #ORM\ManyToMany(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="tripEvents")
* #ORM\JoinTable(name="event_trip_registrators")
*/
private $tripRegistrators;
public function __construct()
{
$this->tripRegistrators = new ArrayCollection();
}
public function getTripRegistrators()
{
return $this->tripRegistrators;
}
public function setTripRegistrators($tripRegistrators)
{
$this->tripRegistrators = $tripRegistrators;
}
public function addTripRegistrator(User $tripRegistrator)
{
$this->tripRegistrators->add($tripRegistrator);
}
public function removeTripRegistrator($tripRegistrator)
{
$this->tripRegistrators->removeElement($tripRegistrator);
}
Second class:
/**
* #ORM\ManyToMany(targetEntity="Bundle\Entity\Event", mappedBy="tripRegistrators")
*/
protected $tripEvents;
public function __construct()
{
parent::__construct();
$this->tripEvents = new ArrayCollection();
}
public function getTripEvents()
{
return $this->tripEvents;
}
public function setTripEvents($tripEvents)
{
$this->tripEvents = $tripEvents;
}
If I call $event->getTripRegistrators() (first class), I only get an empty persistent collection.
Do you have any hint why this happens?
If I save items via SonataAdmin, everything works fine, the database table has correct data.

PHP Symfony3, listener on instantiating a new entity

I would like to trigger on an instantiation of a new entity.
Can someone say if it is possible, and how ?
Waiting for an issue, I bring this solution in getting a new entity in a service… but I find it not enough optimized.
Here is (simplified extract of) my code with an added event "onCreate" in listener :
This is running well, but I have to call a new entity, passing by a service. I want my new entity filled directly while just doing "$entity = new entity();"
abstract class serviceBaseEntity extends serviceBaseClass {
const ENTITY_CLASS = ''; // here class of entity
protected $classname;
protected $shortname;
protected $EntityManager;
protected $ObjectManager;
public function __construct(ContainerInterface $container, RequestStack $request_stack) {
parent::__construct($container, $request_stack);
$this->EntityManager = $this->container->get('doctrine.orm.entity_manager');
$this->ObjectManager = $this->container->get('doctrine')->getManager();
$this->addEventSubscriber(new serviceAnnotations($container, $request_stack));
$this->classname = $this->calledClassname::ENTITY_CLASS;
$this->shortname = (new ReflectionClass($this->classname))->getShortName();
return $this;
}
protected function addEventSubscriber(EventSubscriber $EventSubscriber) {
$this->EntityManager->getEventManager()->addEventSubscriber($EventSubscriber);
return $this;
}
public function getNew() {
$entity = new $this->classname;
$eventArgs = new LifecycleEventArgs($entity, $this->ObjectManager);
$this->EntityManager->getEventManager()->dispatchEvent(AnnotationsBase::onCreate, $eventArgs);
return $entity;
}
Maybe you could use a sort of factory method in your entity, with a private constructor. For example:
Class MyEntity {
private function __construct( ) {
your code
}
public static function createEntity(your params) {
do some stuff
$e=new MyEntity();
do some other stuff
return $e;
}
}

Code completion in injected classes

Here is my sample code which is what I want to get, but in User class I get no codecompletion (for $this->app) in PHPSotrm.
How to change that code to enable code completion? I want to avoid global.
namespace myApp
class app
{
public $pdo = "my PDO";
public function __construct() {
$this->user=new User($this);
$this->test=new Test($this);
echo $this->user->getPDO();
//"my PDO"
echo $this->test->getUserPDO();
//"my PDO"
}
}
class User
{
private $app=null;
public function __construct($app) {
$this->app=$app;
}
public function getPDO() {
return $this->app->pdo;
//no code completion
}
}
class Test
{
private $app=null;
public function __construct($app) {
$this->app=$app;
}
public function getUserPDO() {
return $this->app->user->getPDO;
//no code completion
}
}
There's plenty of information on how to achieve this. All you need to do is to add the PHPDoc describing the property type.
class User
{
/**
* #var App
*/
private $app=null;
/**
* #param App $app
*/
public function __construct($app) {
$this->app = $app;
}
/**
* #return PDO
*/
public function getPDO() {
return $this->app->pdo;
}
}
If properties are implemented via magic getters / setters, you can use the same principles on the class itself.
/**
* #property App $app
* #method Pdo getPdo()
*/
class User
{
// …

ObjectManager The method createQuery() does not seem to exist

I have class like this which works and returns feedbacks from the database
namespace Acme\Bundle\AcmeBundle\Handler;
use Doctrine\Common\Persistence\ManagerRegistry;
class FeedbackHandler implements FeedbackHandlerInterface
{
private $em;
private $entityClass;
private $repository;
public function __construct(ManagerRegistry $mr, $entityClass)
{
$this->em = $mr->getManager();
$this->entityClass = $entityClass;
$this->repository = $this->em->getRepository($this->entityClass);
}
public function get($id)
{
return $this->repository->find($id);
}
public function all($limit = 50, $offset = 0)
{
$feedbacks = $this->em->createQuery("SELECT f FROM AcmeAcmeBundle:Feedback f")
->setFirstResult($offset)
->setMaxResults($limit)
->getResult();
return array( "feedback" => $feedbacks );
}
}
however when I submit code to scrutinizer-ci.com it reports that
The method createQuery() does not seem to exist on
object
same with PhpStorm, it displays warning at this line. Is there an approach to fix this issue?
Add a #var phpdoc comment for the $em variable ...
... or (even better) #return to ManagerRegistry's getManager() method.
Then PHPStorm / scrutinizer-ci will both know of which type $em is and that it has a createQuery() method.
This is a good practice in general and will enable autocompletion in PHPStorm, too.
example:
/** #var \Doctrine\ORM\EntityManager */
private $em;
... or ...
class ManagerRegistry
{
/**
* ...
*
* #return \Doctrine\ORM\EntityManager
*/
public function getManager()
{
// ...

accessing entity manager inside phpunittest

I have the following unit test code in symfony:
<?php
// src/Acme/DemoBundle/Tests/Utility/CalculatorTest.php
namespace Shopious\MainBundle\Tests;
class ShippingCostTest extends \PHPUnit_Framework_TestCase
{
public function testShippingCost()
{
$em = $this->kernel->getContainer()->get('doctrine.orm.entity_manager');
$query = $em->createQueryBuilder();
$query->select('c')
->from("ShopiousUserBundle:City", 'c');
$result = $query->getQuery()->getResult();
var_dump($result);
}
}
and I am trying to access the entity manager here, howver it always gives me this error:
Undefined property: Acme\MainBundle\Tests\ShippingCostTest::$kernel
To achieve this you need to create a base test class (let's call it KernelAwareTest) with following contents:
<?php
namespace Shopious\MainBundle\Tests;
require_once dirname(__DIR__).'/../../../app/AppKernel.php';
/**
* Test case class helpful with Entity tests requiring the database interaction.
* For regular entity tests it's better to extend standard \PHPUnit_Framework_TestCase instead.
*/
abstract class KernelAwareTest extends \PHPUnit_Framework_TestCase
{
/**
* #var \Symfony\Component\HttpKernel\Kernel
*/
protected $kernel;
/**
* #var \Doctrine\ORM\EntityManager
*/
protected $entityManager;
/**
* #var \Symfony\Component\DependencyInjection\Container
*/
protected $container;
/**
* #return null
*/
public function setUp()
{
$this->kernel = new \AppKernel('test', true);
$this->kernel->boot();
$this->container = $this->kernel->getContainer();
$this->entityManager = $this->container->get('doctrine')->getManager();
$this->generateSchema();
parent::setUp();
}
/**
* #return null
*/
public function tearDown()
{
$this->kernel->shutdown();
parent::tearDown();
}
/**
* #return null
*/
protected function generateSchema()
{
$metadatas = $this->getMetadatas();
if (!empty($metadatas)) {
$tool = new \Doctrine\ORM\Tools\SchemaTool($this->entityManager);
$tool->dropSchema($metadatas);
$tool->createSchema($metadatas);
}
}
/**
* #return array
*/
protected function getMetadatas()
{
return $this->entityManager->getMetadataFactory()->getAllMetadata();
}
}
Then your own test class will be extended from this one:
<?php
namespace Shopious\MainBundle\Tests;
use Shopious\MainBundle\Tests\KernelAwareTest;
class ShippingCostTest extends KernelAwareTest
{
public function setUp()
{
parent::setUp();
// Your own setUp() goes here
}
// Tests themselves
}
And then use parent's class methods. In your case, to access entity manager, do:
$entityManager = $this->entityManager;

Categories