Query builder and entity inheritance - symfony

I have entities:
abstract class AbstractEntity
{
private $someField;
}
/**
* ...
* #ORM\Entity(repositoryClass="ConcreteEntityRepository")
*/
class ConcreteEntity extends AbstractEntity
{
private $otherField;
}
class ConcreteEntityRepository extends EntityRepository
{
public function getSomething()
{
$qb = $this->getEntityManager()->createQueryBuilder()
->select('t')
->from('MyBundle:ConcreteEntity', 't');
$result = $query->getResult();
}
}
Result will be with correct count of fields but values of parent class will be null.
How can I correctly get all the fields?
And when I try to use:
->select('t.someField') // Error
->select('t.otherField') // Good

My guess is you can't use private properties in your abstract class. Try using protected ones.
The documentation does the same: http://docs.doctrine-project.org/en/latest/reference/inheritance-mapping.html.

Related

Circular reference for Repository in Symfony 5

I try to follow this tutorial : https://www.thinktocode.com/2018/03/05/repository-pattern-symfony/.
It's suppose to help structure your Repository.
But when i get to this point :
final class ProductRepository
{
/**
* #var EntityManagerInterface
*/
private $entityManager;
/**
* #var ObjectRepository
*/
private $objectRepository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
$this->objectRepository = $this->entityManager->getRepository(Product::class);
}
public function find(int $productId): Product
{
$product = $this->objectRepository->find($productId);
return $product;
}
public function findOneByTitle(string $title): Product
{
$product = $this->objectRepository
->findOneBy(['title' => $title]);
return $product;
}
public function save(Product $product): void
{
$this->entityManager->persist($product);
$this->entityManager->flush();
}
}
And testing my Repository with this test case :
<?php
namespace App\Tests\Repository;
use App\Repository\ProductRepository;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class ProductRepository_KernelTest extends KernelTestCase
{
private ?ProductRepository $_productRepository;
protected function setUp(): void
{
$kernel = self::bootKernel();
$this->_productRepository = self::$container->get(ProductRepository::class);
}
public function test_findAllProductNatByLabelForLabelEmptyReturnTenProduct()
{
dump($this->_productRepository->findAllProductsByLabel('AACIFEMINE'));
die();
}
}
It loop endlessly.
I think it's due to this code :
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
$this->objectRepository = $this->entityManager->getRepository(Product::class); // <-----
}
As it call the ProductRepository constructor inside of this same constructor... So i guess it's why that loop
So I don't know. Is this tutorial just wrong or not up to date ?
https://www.thinktocode.com/2018/03/05/repository-pattern-symfony/#comment-4155200782
Maciej,
You are correct that in these example we are using 2 repositories. The
object repository from doctrine inside our own custom repository. This
allows use to be decoupled from doctrine's repository and still change
this in the future. This means to not set your custom repository as
the default repository in your entity.
You can get rid of inject the object repository, and in so only be
using 1 repository by implementing a BaseRepository class in which you
create the basic findBy, findOneBy, createQueryBuilder yourself. Take
a look at the EntityRepository in Doctrine/ORM. This might be a good
follow up topic to go over in a future article to create a better
solution then I suggested in here.

How to get entity instance when doing unit test using Symfony and PHPUnit?

I'm trying to do a unit test in the environment as the title says. However, the method contained in the class under test requires an entity instance as an argument. So I'm trying to get the above entity in a generic homebrew test class that extends the TestCase class but I can't figure out how to do it.
I have little experience with functional testing. I used fixtures at that time, so I'm guessing that I should use fixtures this time too. Is that correct? I would appreciate if you could teach me how to do that as well.
Please let me know, even a little information.
below is the test class, and the interface of the test target.
ps. we are using nelmio/alice bundle.
<?php
declare(strict_types=1);
namespace xxx\xxxx\Tests\Customize\Server\AlladinOffice;
use Codeception\PHPUnit\TestCase;
use Customize\Service\AlladinOffice\CodeFormatter;
use Customize\Service\AlladinOffice\CustomerCode;
use Doctrine\Common\Persistence\ObjectManager;
use Eccube\Repository\CustomerRepository;
use PHPUnit\Framework\MockObject\MockObject;
use Proxies\__CG__\Eccube\Entity\Customer;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class CustomerCodeTest extends KernelTestCase
{
/**
* #var CustomerCode
*/
private $sut;
/**
* #var CustomerRepository|MockObject
*/
private $customerRepository;
protected function setUp()
{
$codeFormatter = new CodeFormatter();
$this->sut = new CustomerCode($codeFormatter);
$this->customerRepository = $this->createMock(CustomerRepository::class);
parent::setUp();
}
public function testGetCustomerCode()
{
$actual = $this->sut->getCustomerCode(null);
$expected = 1000000000;
$this->assertEquals($expected, $actual);
// $customer = $this->customerRepository->findBy([], ['id' => 'ASC']);
// dump($customer);exit();
// $actual = $this->sut->getCustomerCode($customer);
$customer = new Customer;
//$customer->set
//$this->customerRepository
//->expects($this->any())
//->method('find')
//->willReturn($);
}
}
<?php
declare(strict_types=1);
namespace Customize\Service\AlladinOffice;
use Eccube\Entity\Customer;
interface CustomerCodeInterface
{
public function getCustomerCode(?Customer $customer): string;
public function getChannelCode(?Customer $customer): string;
public function getRankCode(?Customer $customer): string;
}
for example a detailed method to test.
public function getCustomerCode(?Customer $customer): string
{
if (!$customer) {
return self::DEFAULT_CUSTOMER_CODE;
}
$customerRank = $customer->getXxxCustomer()->getCustomerRank();
$customerChannel = $customerRank ? $customerRank->getCustomerChannel() : null;
if (!$customerChannel || !$customerChannel->isExportToAO()) {
return self::DEFAULT_CUSTOMER_CODE;
}
return $this->formatter->getCustomerCodeFromCustomerId($customer->getId());
}

Example Usage of ObjectManagerAware inside entity

I would like to use entity manager inside entity and no idea for usage.
use Doctrine\Common\Persistence\ObjectManagerAware;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use SomeBundle\Entity\Boarding;
use SomeBundle\Entity\User;
class Entity extends ApiUserEntity implements ObjectManagerAware
{
private $em;
public function ___construct(User $user)
{
$this->board = $this->getData(123);
}
public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata)
{
$this->em = $objectManager;
}
private function getData($leadId)
{
//return gettype($this->em); //return null
$repository =$this->em->getRepository(Boarding::class);
$query = $repository->createQueryBuilder('b')
->where('b.lead = :lead')
->setParameter('lead', $leadId)
->getQuery();
$boards = $query->getResult();
return $boards;
}
}
Using this code get me error
Call to a member function getRepository() on null"
The entity manager is null also
//return gettype($this->em); //return null
Any idea for example usage?
You can try to create a repository like here. Just add
* #ORM\Entity(repositoryClass="App\Repository\EntityRepository")
or to YAML, Xml depends on your configuration and then create the repository file. Like this one:
// src/AppBundle/Repository/ProductRepository.php
namespace AppBundle\Repository;
use Doctrine\ORM\EntityRepository;
class ProductRepository extends EntityRepository
{
public function findAllOrderedByName()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AppBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
}

__invoke in Symfony

I want to split my codebase to simple one purpose specific classes like:
class AddKeyword
{
/**
* #var KeywordRepository
*/
private $keywordRepository;
public function __construct(KeywordRepository $keywordRepository)
{
$this->keywordRepository = $keywordRepository;
}
public function __invoke(string $name): Keyword
{
$entity = $this->keywordRepository->findOneByName($name);
if ($entity)
return $entity;
$entity = Keyword::create(KeywordId::create(), $name);
$this->keywordRepository->save($entity);
return $entity;
}
}
But for using that class I have to resolve DI. How to do it?
Thank you in advance.
Not sure about what you want to achieve but if you want add/get keyword everywhere in your code base you have 2 choices:
Use autowiring
Declare your class as service and get it from the container.
Symfony encourage DI by autowiring.
namespace App\Controller;
use App\AddKeyword;
class DefaultController
{
public function __construct(Addkeyword $keyword) {
$keyword('keyword');
}
}

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