test a repository in symfony - php

I have the following method in my repository and I want to test it
public function myFindOne($id)
{
// On passe par le QueryBuilder vide de l'EntityManager pour l'exemple
$qb = $this->_em->createQueryBuilder();
$qb->select('a')
->from('xxxBundle:entity', 'a')
->where('a.id = :id')
->setParameter('id', $id);
return $qb->getQuery()
->getResult();}
I found the following code in the documentation, but I could not understand it
// src/Acme/StoreBundle/Tests/Entity/ProductRepositoryFunctionalTest.php
namespace Acme\StoreBundle\Tests\Entity;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class ProductRepositoryFunctionalTest extends WebTestCase
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $em;
/**
* {#inheritDoc}
*/
public function setUp()
{
static::$kernel = static::createKernel();
static::$kernel->boot();
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager()
;
}
public function testSearchByCategoryName()
{
$products = $this->em
->getRepository('AcmeStoreBundle:Product')
->searchByCategoryName('foo')
;
$this->assertCount(1, $products);
}
/**
* {#inheritDoc}
*/
protected function tearDown()
{
parent::tearDown();
$this->em->close();
}
}

To see what you should edit in this code, the testSearchByCatergory() should be a good start. In this example, it gets the result of the tested method into $products and checks that this collection contains only one element.
So I guess your test would be to test that the returned object is the one you expect to be returned. But heh, like #cheesemacfly said, your repo is kinda the same as findOne[ById]()... Oh and BTW, you should check up phpunit [EN] (Or in FR, as I saw in your comment) documentation to see how you should make it run.
Cheers. :)

From Symfony's official documentation, the Repository methods should be tested as follow:
// tests/AppBundle/Entity/ProductRepositoryTest.php
namespace Tests\AppBundle\Entity;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class ProductRepositoryTest extends KernelTestCase
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $em;
/**
* {#inheritDoc}
*/
protected function setUp()
{
self::bootKernel();
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager();
}
public function testSearchByCategoryName()
{
$products = $this->em
->getRepository('AppBundle:Product')
->searchByCategoryName('foo')
;
$this->assertCount(1, $products);
}
/**
* {#inheritDoc}
*/
protected function tearDown()
{
parent::tearDown();
$this->em->close();
}
}

Related

Configuring HTTP Request Methods in Symfony?

I'm trying to get into Symfony for a project and I (sort of) have trouble figuring out how things work in Symfony. I've done these things a couple of times in Java Spring Boot before but this project needs me doing stuff in PHP and I want to learn too.
What I have basically done is create an Entity, Repository, Service and now I'm working on the controller.
I made the entity, repo and controller using make:entity (or make:controller)
The Service is supposed to wrap the repository and further abstract things.
My questions:
In the Controller I have a constructor. Is that one actually called? I need it to initialize the Service it is used in
How do I define what HTTP Request method needs to be used? I know how to specify routes, but how do I define if it is to be accessed as GET, POST, PUT, DELETE? The Symfony doc about controllers does not specify this.
I have to find this out later so I think I'll ask here: If I want to persist an item through the api, I just pass the objects as json? (For example if I'm testing with postman)
Here's my Entity:
?php
namespace App\Entity;
use App\Repository\FahrzeugRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=FahrzeugRepository::class)
*/
class Fahrzeug
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $fahrgestellnummer;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $tueren;
/**
* #ORM\Column(type="string", length=255)
*/
private $modellbezeichnung;
/**
* #ORM\ManyToMany(targetEntity=Person::class, mappedBy="faehrt")
*/
private $gefahren_von;
/**
* #ORM\ManyToOne(targetEntity=Marke::class, inversedBy="produziert")
* #ORM\JoinColumn(nullable=false)
*/
private $stammt_von;
public function __construct()
{
$this->gefahren_von = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getFahrgestellnummer(): ?string
{
return $this->fahrgestellnummer;
}
public function setFahrgestellnummer(string $fahrgestellnummer): self
{
$this->fahrgestellnummer = $fahrgestellnummer;
return $this;
}
public function getTueren(): ?int
{
return $this->tueren;
}
public function setTueren(?int $tueren): self
{
$this->tueren = $tueren;
return $this;
}
public function getModellbezeichnung(): ?string
{
return $this->modellbezeichnung;
}
public function setModellbezeichnung(string $modellbezeichnung): self
{
$this->modellbezeichnung = $modellbezeichnung;
return $this;
}
/**
* #return Collection|Person[]
*/
public function getGefahrenVon(): Collection
{
return $this->gefahren_von;
}
public function addGefahrenVon(Person $gefahrenVon): self
{
if (!$this->gefahren_von->contains($gefahrenVon)) {
$this->gefahren_von[] = $gefahrenVon;
$gefahrenVon->addFaehrt($this);
}
return $this;
}
public function removeGefahrenVon(Person $gefahrenVon): self
{
if ($this->gefahren_von->removeElement($gefahrenVon)) {
$gefahrenVon->removeFaehrt($this);
}
return $this;
}
public function getStammtVon(): ?Marke
{
return $this->stammt_von;
}
public function setStammtVon(?Marke $stammt_von): self
{
$this->stammt_von = $stammt_von;
return $this;
}
}
My Repo:
<?php
namespace App\Repository;
use App\Entity\Fahrzeug;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* #method Fahrzeug|null find($id, $lockMode = null, $lockVersion = null)
* #method Fahrzeug|null findOneBy(array $criteria, array $orderBy = null)
* #method Fahrzeug[] findAll()
* #method Fahrzeug[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class FahrzeugRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Fahrzeug::class);
}
// /**
// * #return Fahrzeug[] Returns an array of Fahrzeug objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('f')
->andWhere('f.exampleField = :val')
->setParameter('val', $value)
->orderBy('f.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Fahrzeug
{
return $this->createQueryBuilder('f')
->andWhere('f.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
My Service
<?php
namespace App\Service;
use App\Entity\Fahrzeug;
use App\Repository\FahrzeugRepository;
use Doctrine\ORM\EntityManagerInterface;
class FahrzeugService {
private FahrzeugRepository $fahrzeugRepository;
public function __construct() {
$this->injectRepository();
}
private function injectRepository() {
$this->fahrzeugRepository = $this->getDoctrine()->getManager()->getRepository(Fahrzeug::class);
}
public function findById(int $id): Fahrzeug {
return $this->fahrzeugRepository->find($id);
}
//Returns an array
public function findAll(): array
{
return $this->fahrzeugRepository->findAll();
}
public function save(Fahrzeug $fahrzeug): Fahrzeug {
$this->fahrzeugRepository->persist($fahrzeug);
//TODO: gucken ob persist reicht oder ob man eine neue instanz erzeugen muss
$this->fahrzeugRepository->flush();
return $fahrzeug;
}
//TODO UPdate - kann man das auch mittels save machen?
public function delete(Fahrzeug $fahrzeug): Fahrzeug {
/*TODO: Herausfinden was auf der anderen Seite passiert
Idealerweise wird auf der anderen Seite das Feld genullt
*/
$this->fahrzeugRepository->remove($fahrzeug);
$this->fahrzeugRepository->flush();
return $fahrzeug;
}
}
My Controller I'm working on:
<?php
namespace App\Controller;
use App\Entity\Fahrzeug;
use App\Service\FahrzeugService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class FahrzeugController extends AbstractController
{
private FahrzeugService $fahrzeugService;
//TODO wird der Controller initialisiert?
public function __construct() {
$this->fahrzeugService = new FahrzeugService();
}
#[Route('/fahrzeugIndex', name: 'fahrzeug')]
public function index(): Response
{
return $this->render('fahrzeug/index.html.twig', [
'controller_name' => 'FahrzeugController',
]);
}
#[Route('/fahrzeug/{id}', name: 'fahrzeug')]
public function findById(int $id): Fahrzeug {
return $this->fahrzeugService->findById($id);
}
#[Route('/fahrzeug', name: 'fahrzeug')]
public function findAll(): array {
return $this->fahrzeugService->findAll();
}
#[Route('/fahrzeugIndex', name: 'fahrzeug')]
public function save(Fahrzeug $fahrzeug): Fahrzeug {
return $this->fahrzeugService->save($fahrzeug);
}
public function delete(Fahrzeug $fahrzeug): Fahrzeug {
return $this->fahrzeugService->delete($fahrzeug);
}
}
Dependency Injection in PHP/Symfony works different than in Java/Spring. If you want to inject a dependency, you have to add it to the constructor. That dependency will be automatically constructed using the dependency-injection-container built in symfony.
private FahrzeugService $fahrzeugService;
public function __construct(FahrzeugService $fahrzeugService)
{
$this->fahrzeugService = $fahrzeugService;
}
You don't have to specify a method which injects the service. It is possible to change how your class (Controller/Service/etc.) is being created, but that's not the case here.
Once that's done, you can call methods in your FahrzeugService using
$this->fahrzeugService->[method]()
So in your case, you can use:
<?php
namespace App\Service;
use App\Entity\Fahrzeug;
use App\Repository\FahrzeugRepository;
class FahrzeugService {
private FahrzeugRepository $fahrzeugRepository;
public function __construct(FahrzeugRepository $fahrzeugRepository) {
$this->fahrzeugRepository = $fahrzeugRepository;
}
//...
}
You don't have to get the repository from the EntityManager. It is possible, but you don't have to.
For your HTTP methods. You can specify the method in your annotations. Since you are already using the new annotations, you can use
#[Route('/fahrzeugIndex', name: 'fahrzeug', methods:['GET', 'POST'])]

phpunit Functional Tests Failed getRepository

I'm running phpunit test but, I can't use getRepository to get $amount from my databases.
I don't know how to fix it.
I get this
1) Tests\BankBundle\Controller\BankControllerTest::testMoneyIn
Failed asserting that null matches expected 50
<?php
namespace Tests\BankBundle\Controller;
use BankBundle\Entity\entry;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class BankControllerTest extends WebTestCase
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $em;
/**
* {#inheritDoc}
*/
public function setUp(): void
{
self::bootKernel();
$this->em = static::$kernel->getContainer()
->get('doctrine')
->getManager();
}
public function testMoneyIn()
{
$client = static::createClient();
$client->request('POST', '/bank/moneyin', array('amount' => 50));
$query = $this->em
->getRepository('BankBundle:entry')
->getAmount();
$this->assertEquals(50, $query);
}

Call to a member function on null

I create service for add formType then persist object and in controller I invoke data but I have error shown on below image:
in controller i extend class abstractController content getHandler and I have view newskill.html.twig
Code SkillController.php:
<?php
namespace AppBundle\Controller\Condidate;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Entity\Skill;
use AppBundle\Controller\AbstractController;
use AppBundle\Form\SkillType;
/**
*Class SkillController.
*/
class SkillController extends AbstractController
{
/**
*function handler.
*/
protected function getHandler(){
//var_dump('test');
}
/**
*function addSkill
* #param Request $request
* #return \Symfony\Component\Form\Form The form
*/
public function addSkillAction(Request $request) {
$skill = $this->getHandler()->post();
if ($skill instanceof \AppBundle\Entity\Skill) {
return $this->redirectToRoute('ajouter_info');
}
return $this->render('skills/newskill.html.twig', array(
'form' => $form->createView(),));
}
}
Code SkillHandler.php:
<?php
namespace AppBundle\Handler;
use AppBundle\Handler\HandlerInterface;
use Symfony\Component\HttpFoundation\Request;
use AppBundle\Entity\Skill;
use Doctrine\ORM\EntityManager;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Form\formFactory;
/**
* SkillHandler.
*/
class SkillHandler implements HandlerInterface {
/**
*
* #var EntityManager
*/
protected $em;
/**
*
* #var formFactory
*/
private $formFactory;
/**
*function construct.
*/
public function __construct(EntityManager $entityManager, formFactory $formFactory)
{
$this->em = $entityManager;
$this->formFactory = $formFactory;
}
/**
*function post
*/
public function post(array $parameters, array $options = []) {
$form = $this->formFactory->create(\AppBundle\Form\SkillType::class, $object, $options);
$form->submit($parameters);
if ($form->isValid()) {
$skill = $form->getData();
$this->persistAndFlush($skill);
return $skill;
}
return $form->getData();
}
/**
*function persisteAndFlush
*/
protected function persistAndFlush($object) {
$this->em->persist($object);
$this->em->flush();
}
/**
*function get
*/
public function get($id){
throw new \DomainException('Method SkillHandler::get not implemented');
}
/**
*function all
*/
public function all($limit = 10, $offset = 0){
throw new \DomainException('Method SkillHandler::all not implemented');
}
/**
*function put
*/
public function put($resource, array $parameters, array $options){
throw new \DomainException('Method SkillHandler::put not implemented');
}
/**
*function patch
*/
public function patch($resource, array $parameters, array $options){
throw new \DomainException('Method SkillHandler::patch not implemented');
}
/**
*function delete
*/
public function delete($resource){
throw new \DomainException('Method SkillHandler::delete not implemented');
}
}
code services.yml:
skill_add:
class: AppBundle\Handler\SkillHandler
arguments:
- "#doctrine.orm.entity_manager"
- "#form.factory"
public: true
Any help would be appreciated.
Your $this->getHandler() retruns null.
Solution can be checking if $this->getHandler() doesn't return null in first place.
if (!$this->getHandler()) {
throw new \Exception(sprintf('Handler cannot be null')
} else {
$skill = $this->getHandler()->post();
}
Try this, firstly you should take your handler into getHandler() method at your Controller.
protected function getHandler(){
return $this->get('skill_add');
}

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