phpspec doctrine queryBuilder - mock - symfony

I'm writing a test in PHPSpec 2.0.0 that would use Doctrine query builder in a Symfony application. This is my class:
class RedirectHandle
{
/**
* #var string
*/
private $kernelEnvironment;
/**
* #var ContainerInterface
*/
private $container;
/**
* RedirectHandle constructor.
* #param $env
* #param ContainerInterface $containerInterface
*/
public function __construct($env, ContainerInterface $containerInterface)
{
$this->kernelEnvironment = $env;
$this->container = $containerInterface;
}
public function handleUrl($url)
{
if ($this->kernelEnvironment === "dev") {
$em = $this->container->get("doctrine")->getEntityManager();
return $em->createQuery("SELECT a FROM module_redirect a WHERE url_from_redirect = :url ")
->setParameter('url', $url)
->getSingleScalarResult();
}
return false;
}
}
Here is my phpspec test:
class RedirectHandleSpec extends ObjectBehavior
{
function let($kernel,$container,$queryBuilder,$em,$redirectHandle)
{
$env = "dev";
$kernel->beADoubleOf('Symfony\Component\HttpKernel\Kernel');
$kernel->getEnvironment()->willReturn($env);
$queryBuilder->beADoubleOf('Doctrine\ORM\QueryBuilder');
$container->beADoubleOf('Symfony\Component\DependencyInjection\ContainerInterface');
$redirectHandle->beADoubleOf('Kei\WebsiteBundle\Tools\Redirect\RedirectHandle');
$em->beADoubleOf('Doctrine\ORM\EntityManager');
$kernel->getContainer()->willReturn($container);
$this->beConstructedWith($env,$container);
}
function it_is_initializable()
{
$this->shouldHaveType('Kei\WebsiteBundle\Tools\Redirect\RedirectHandle');
}
function it_is_init_redirect_when_env_is_dev($container,$queryBuilder,$em)
{
$container->get("doctrine")->willReturn($queryBuilder);
$em->createQuery(Argument::any())->willReturn($em);
$this->handleUrl("test")->shouldBeReturn(true);
}
}
When I ran the test, I got the following error:
1PHP Fatal error: Uncaught Error: Call to a member function createQuery() on null in /var/www/kei-site/src/Kei/WebsiteBundle/Tools/Redirect/RedirectHandle.php:37
Stack trace:
#0 [internal function]: Kei\WebsiteBundle\Tools\Redirect\RedirectHandle->handleUrl('test')
#1 /var/www/kei-site/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Caller.php(260): call_user_func_array(Array, Array)
#2 /var/www/kei-site/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject/Caller.php(97): PhpSpec\Wrapper\Subject\Caller->invokeAndWrapMethodResult(Object(Kei\WebsiteBundle\Tools\Redirect\RedirectHandle), 'handleUrl', Array)
#3 /var/www/kei-site/vendor/phpspec/phpspec/src/PhpSpec/Wrapper/Subject.php(187): PhpSpec\Wrapper\Subject\Caller->call('handleUrl', Array)
#4 [internal function]: PhpSpec\Wrapper\Subject->__call('handleUrl', Array)
#5 /var/www/kei-site/vendor/phpspec/phpspec/src/PhpSpec/ObjectBehavior.php(136): call_user_func_array(Array, Array)
#6 /var/www/kei-site/spec/Kei/WebsiteBundle/Tools/Redirect/RedirectHandleSpec.php(40): PhpSpec\Obj in /var/www/kei-site/src/Kei/WebsiteBundle/Tools/Redirect/RedirectHandle.php on line 37
What should I do to fix this?

class RedirectHandle
{
/**
* #var string
*/
private $kernelEnvironment;
/**
* #var
*/
private $container;
/**
* RedirectHandle constructor.
* #param $env
* #param ContainerInterface $containerInterface
*/
public function __construct($env,ContainerInterface $containerInterface)
{
$this->kernelEnvironment = $env;
$this->container = $containerInterface;
}
/**
*
*/
public function handleUrl($url)
{
if ($this->kernelEnvironment === "dev") {
$em = $this->container->get("doctrine")->getEntityManager();
$query = $em->createQuery("SELECT a FROM KeiWebsiteBundle:Carrier a");
return true;
}
return false;
}
}
Phpspec code after refactor:
class RedirectHandleSpec extends ObjectBehavior
{
function let($kernel,$container,$queryBuilder,$em)
{
$env = "dev";
$kernel->beADoubleOf('Symfony\Component\HttpKernel\Kernel');
$queryBuilder->beADoubleOf('Doctrine\ORM\QueryBuilder');
$container->beADoubleOf('Symfony\Component\DependencyInjection\ContainerInterface');
$em->beADoubleOf('Doctrine\ORM\EntityManager');
$this->beConstructedWith($env,$container);
}
function it_is_initializable()
{
$this->shouldHaveType('Kei\WebsiteBundle\Tools\Redirect\RedirectHandle');
}
/**
* Przekierowuje strone jesli srodowisko jest dev
*/
function it_is_init_redirect_when_env_is_dev($container,$queryBuilder,$em)
{
$container->get("doctrine")->willReturn($queryBuilder);
$em->createQuery(Argument::any())->willReturn(new Query($em->getWrappedObject()));
$this->handleUrl("test");
}
}

Related

Modify POST data before storing in database with Symfony and API-Platform

I'm fairly new to the Symfony universe, so please bear with me if this question has already been answered.
I have provided endpoints with the api-platform to create a RegistrationRequest. A RegistrationRequest has a user that is connected via a ManyToOne relation, so it is stored in another table. In the API, the user or user_id is read-only, this is why the user can not be set in the post data. If a RegistrationRequest is made via the API, the creation fails because the user_id is obviously null.
This is why I would like to set the user manually after a registration request is made via the API but before the RegistrationRequest is stored in the database.
The user is known via the global REMOTE_USER from where I can derive the corresponding user object.
src/Entity/RegistrationRequest.php:
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=App\Repository\RegistrationRequestRepository::class)
* #ApiResource(
* normalizationContext={"groups" = {"read"}},
* denormalizationContext={"groups" = {"write"}},
* paginationEnabled=false,
* )
*/
class RegistrationRequest
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read"})
*/
private $id;
/**
* #ORM\Column(type="string", length=64, nullable=true)
* #Groups({"read", "write"})
*/
private $opt_email;
/**
* #ORM\Column(type="string", length=255, nullable=true)
* #Groups({"read", "write"})
*/
private $title;
/**
* #Gedmo\Mapping\Annotation\Timestampable(on="create")
* #ORM\Column(type="datetime")
* #Groups({"read"})
*/
private $created_at;
/**
* #Gedmo\Mapping\Annotation\Timestampable(on="update")
* #ORM\Column(type="datetime")
* #Groups({"read"})
*/
private $updated_at;
/**
* #ORM\Column(type="text", nullable=true)
* #Groups({"read", "write"})
*/
private $notes;
/**
* #ORM\Column(type="string", length=16)
* #Groups({"read", "write"})
*/
private $language_code;
/**
* #ORM\Column(type="text")
* #Groups({"read", "write"})
*/
private $data;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="registrationRequests")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
public function getId(): ?int
{
return $this->id;
}
public function getOptEmail(): ?string
{
return $this->opt_email;
}
public function setOptEmail(?string $opt_email): self
{
$this->opt_email = $opt_email;
return $this;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(?string $title): self
{
$this->title = $title;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->created_at;
}
public function setCreatedAt(\DateTimeInterface $created_at): self
{
$this->created_at = $created_at;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updated_at;
}
public function setUpdatedAt(\DateTimeInterface $updated_at): self
{
$this->updated_at = $updated_at;
return $this;
}
public function getNotes(): ?string
{
return $this->notes;
}
public function setNotes(?string $notes): self
{
$this->notes = $notes;
return $this;
}
public function getLanguageCode(): ?string
{
return $this->language_code;
}
public function setLanguageCode(string $language_code): self
{
$this->language_code = $language_code;
return $this;
}
public function getData(): ?string
{
return $this->data;
}
public function setData(string $data): self
{
$this->data = $data;
return $this;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
}
src/Controller/RegistrationRequestController.php:
<?php
namespace App\Controller;
use App\Service\IGSNService;
use App\Entity\RegistrationRequest;
use App\Form\RegistrationRequestType;
use App\Repository\RegistrationRequestRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* #Route("/registration_request")
*/
class RegistrationRequestController extends AbstractController
{
/**
* #Route("/")
*/
public function indexNoLocale(): Response
{
return $this->redirectToRoute('app_registration_request_index', ['_locale' => 'de']);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/", name="app_registration_request_index", methods={"GET"})
*/
public function index(RegistrationRequestRepository $registrationRequestRepository): Response
{
return $this->render('registration_request/index.html.twig', [
'registration_requests' => $registrationRequestRepository->findAll(),
]);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/new", name="app_registration_request_new", methods={"GET", "POST"})
*/
public function new(Request $request, RegistrationRequestRepository $registrationRequestRepository, IGSNService $igsnService, TranslatorInterface $translator): Response
{
$registrationRequest = new RegistrationRequest();
$form = $this->createForm(RegistrationRequestType::class, $registrationRequest);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$json_data = json_decode($registrationRequest->getData());
$err = $igsnService->validate_data($json_data);
if ($json_data !== null && empty($err)) {
$registrationRequestRepository->add($registrationRequest, true);
$this->addFlash(
'success',
$translator->trans('Your changes were saved!')
);
return $this->redirectToRoute('app_registration_request_index', [], Response::HTTP_SEE_OTHER);
} else {
$this->addFlash(
'schema_error',
$err
);
}
}
return $this->renderForm('registration_request/new.html.twig', [
'registration_request' => $registrationRequest,
'form' => $form,
]);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/{id}", name="app_registration_request_show", methods={"GET"})
*/
public function show(RegistrationRequest $registrationRequest): Response
{
return $this->render('registration_request/show.html.twig', [
'registration_request' => $registrationRequest,
]);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/{id}/edit", name="app_registration_request_edit", methods={"GET", "POST"})
*/
public function edit(Request $request, RegistrationRequest $registrationRequest, RegistrationRequestRepository $registrationRequestRepository, IGSNService $igsnService, TranslatorInterface $translator): Response
{
$form = $this->createForm(RegistrationRequestType::class, $registrationRequest);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$json_data = json_decode($registrationRequest->getData());
$err = $igsnService->validate_data($json_data);
if ($json_data !== null && empty($err)) {
$registrationRequestRepository->add($registrationRequest, true);
$this->addFlash(
'success',
$translator->trans('Your changes were saved!')
);
return $this->redirectToRoute('app_registration_request_index', [], Response::HTTP_SEE_OTHER);
} else {
$this->addFlash(
'schema_error',
$err
);
}
}
return $this->renderForm('registration_request/edit.html.twig', [
'registration_request' => $registrationRequest,
'form' => $form,
]);
}
/**
* #Route("/{_locale<%app.supported_locales%>}/{id}", name="app_registration_request_delete", methods={"POST"})
*/
public function delete(Request $request, RegistrationRequest $registrationRequest, RegistrationRequestRepository $registrationRequestRepository, TranslatorInterface $translator): Response
{
if ($this->isCsrfTokenValid('delete' . $registrationRequest->getId(), $request->request->get('_token'))) {
$registrationRequestRepository->remove($registrationRequest, true);
$this->addFlash(
'success',
$translator->trans('Request successfully deleted!')
);
}
return $this->redirectToRoute('app_registration_request_index', [], Response::HTTP_SEE_OTHER);
}
}
config/packages/api_platform.yaml
api_platform:
mapping:
paths: ['%kernel.project_dir%/src/Entity']
patch_formats:
json: ['application/merge-patch+json']
swagger:
versions: [3]
# Fixes empty api endpoint list with error:
# No operations defined in spec!
# See https://github.com/api-platform/core/issues/4485
metadata_backward_compatibility_layer: false
I have now got it solved with a DataPersister, as suggested by Julien B. in the comments.
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use App\Entity\RegistrationRequest;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Security;
final class RequestDataPersister implements DataPersisterInterface
{
private $security;
public function __construct(Security $security, EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
$this->security = $security;
}
public function supports($data): bool
{
return $data instanceof RegistrationRequest;
}
public function persist($data)
{
$data->setUser($this->security->getUser());
$this->entityManager->persist($data);
$this->entityManager->flush();
return $data;
}
public function remove($data): void
{
// no action needed
}
}

How to mock variables in a PHPUnit Coverage Test?

I'm writing PHPUnit tests and running coverage tests. I admit its very difficult to have 100% coverage, however, I'd like to get as close as possible. In a following scenario, how to mock variables in a clause in order to test the code block?
class CalendarClientService
{
/** #var array SCOPES */
public const SCOPES = [Google_Service_Calendar::CALENDAR];
/** #var string ACCESS_TYPE */
public const ACCESS_TYPE = "offline";
/** #var string CALENDAR_ID */
public const CALENDAR_ID = "primary";
/** #var int MAX_RESULTS */
public const MAX_RESULTS = 25;
/** #var string ORDER_BY */
public const ORDER_BY = "startTime";
/** #var bool SINGLE_EVENTS */
public const SINGLE_EVENTS = true;
/** #var string|null TIME_MIN */
public const TIME_MIN = null;
/** #var bool CACHE_TIME_TO_LIVE */
public const CACHE_TIME_TO_LIVE = 604800;
/** #var string */
public string $clientSecretPath = "";
/** #var StorageAdapterFactoryInterface */
protected StorageAdapterFactoryInterface $storageAdapterFactory;
/** #var StorageInterface */
protected StorageInterface $storageInterfaceCache;
/**
* CalendarClientService constructor.
* #param string $clientSecretPath
* #param StorageAdapterFactoryInterface $storageAdapterFactory
* #param StorageInterface $storageInterfaceCache
*/
public function __construct(
string $clientSecretPath,
StorageAdapterFactoryInterface $storageAdapterFactory,
StorageInterface $storageInterfaceCache
) {
$this->clientSecretPath = $clientSecretPath;
$this->storageAdapterFactory = $storageAdapterFactory;
$this->storageInterfaceCache = $storageInterfaceCache;
}
/** #return string */
public function getClientSecretPath()
{
return $this->clientSecretPath;
}
/** #param string $secretFile */
public function setClientSecretPath(string $secretFile)
{
$this->clientSecretPath = $secretFile;
}
/**
* #param array
* #return Google_Service_Calendar_Event
*/
public function getGoogleServiceCalendarEvent($eventData)
{
return new Google_Service_Calendar_Event($eventData);
}
/**
* #param string
* #return Google_Service_Calendar_EventDateTime
*/
public function getGoogleServiceCalendarEventDateTime($dateTime)
{
$eventDateTime = new Google_Service_Calendar_EventDateTime();
$eventDateTime->setDateTime(Carbon::parse($dateTime)->toW3cString());
$eventDateTime->setTimeZone(Carbon::parse($dateTime)->timezone->getName());
return $eventDateTime;
}
/**
* #param Google_Client $client
* #return Events
*/
public function getGoogleServiceCalendarResourceEvents(Google_Client $client)
{
$service = new Google_Service_Calendar($client);
return $service->events;
}
/**
* #param int
* #return array
* #throws Exception
* #throws ExceptionInterface
*/
public function getEventData($id)
{
$client = $this->getClient();
if (!$this->authenticateClient($client)) {
return [
"error" => "authentication",
"url" => filter_var($client->createAuthUrl(), FILTER_SANITIZE_URL),
];
}
$service = $this->getGoogleServiceCalendarResourceEvents($client);
return ["event" => $service->get(self::CALENDAR_ID, $id)];
}
/**
* #return Google_Client
* #throws Exception
*/
public function getClient()
{
$client = new Google_Client();
$client->setApplicationName(Module::MODULE_NAME);
$client->setScopes(self::SCOPES);
$client->setAuthConfig($this->clientSecretPath);
$client->setAccessType(self::ACCESS_TYPE);
return $client;
}
/**
* #param Google_Client $client
* #return bool
* #throws ExceptionInterface
*/
public function authenticateClient(Google_Client $client)
{
if ($this->storageInterfaceCache->hasItem("api_access_token")) {
$accessToken = json_decode($this->storageInterfaceCache->getItem("api_access_token"), true);
if ($accessToken["error"] == "invalid_grant" || empty($accessToken)) {
$this->storageInterfaceCache->removeItem("api_access_token");
} else {
$this->storageInterfaceCache->setItem("api_access_token", json_encode($accessToken));
$client->setAccessToken($accessToken);
}
}
if ($client->isAccessTokenExpired()) {
$tokenValid = false;
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$accessToken = $client->getAccessToken();
$this->storageInterfaceCache->setItem("api_access_token", json_encode($accessToken));
$tokenValid = true;
} else {
$helper = new Helper();
if(!$helper->verifyAuthCode($_GET["code"])){
return $tokenValid;
}
$authCode = $_GET["code"];
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
if ($accessToken["error"] == "invalid_grant" || empty($accessToken)) {
$this->storageInterfaceCache->removeItem("api_access_token");
} else {
$this->storageInterfaceCache->setItem("api_access_token", json_encode($accessToken));
$client->setAccessToken($accessToken);
$tokenValid = true;
}
}
} else {
$tokenValid = true;
}
return isset($tokenValid) ? $tokenValid : false;
}
I want to test this 6th line from top in authenticateClient method and want to mock this clause $accessToken["error"] == "invalid_grant" || empty($accessToken).
Now how to go about it?
Edit: Here's a test that I've written. Now whatever value I'm mocking in the $this->storageInterfaceCacheMock->method("getItem"), it always returns empty $accessToken. I've also attached the image for better understanding of what's happening and what I want.
public function testGetEventDataReturnsArrayOnSuccessfulAuthenticateClientThroughCache()
{
$this->storageInterfaceCacheMock->method("hasItem")->willReturn(true);
$this->storageInterfaceCacheMock->method("getItem")->willReturn(json_encode('{"access_token":"ya29.a0ARrdaM99pJTf1XzmD1ngxAH3XJud8lvHb0aTaOOABYdfdhsdfgsdfgVD9OoH4heiKoskDF7DMkHj1_aPuWIO5TE14KHJidFf66xwn_pTCkkSow6Kg4lRHwGrNQBQGI8sPlgnFO5U5hJvYdqgxDMHEqw1TER2w","expires_in":3599,"scope":"https:\/\/www.googleapis.com\/auth\/calendar","token_type":"Bearer","created":1637312218,"refresh_token":"1\/\/03psr5omKiljUCgYFDHDGJHGSHSNwF-L9Iraor5zcfe-h3BeCHSFGSDFGDGJHjy4UnEtKj974LXthS5bWexQcjviVGfJsdfGHSHgIrDn6Yk"}'));
$this->assertIsArray($this->calendarClientService->getEventData(1));
}
Another test which isn't performing as per required is mentioned below. (also visible in the screenshot)
public function testAccessTokenIsExpiredAndGotRefreshToken()
{
$this->googleClientMock->method("isAccessTokenExpired")->willReturn(true);
$this->googleClientMock->method("getRefreshToken")->willReturn(true);
$this->googleClientMock->method("fetchAccessTokenWithRefreshToken")->willReturnSelf();
$this->googleClientMock->method("getAccessToken")->willReturnSelf();
$this->assertTrue($this->calendarClientService->authenticateClient($this->googleClientMock));
}
I believe you want json_decode you don't need double encode here:
$this->storageInterfaceCacheMock->method("getItem")->willReturn(json_encode('{"access_token":"...."}'));
just
$this->storageInterfaceCacheMock->method("getItem")->willReturn('{"access_token":"...."}');
Here's how I resolved the issue and got the test to be successful.
I declared a dummy ACCESS_TOKEN as below and then used in the test method.
class CalendarControllerTest extends AbstractApplicationTestCase
{
/** #var string CLIENT_SECRET */
public const CLIENT_SECRET = __DIR__ . "/../_fixtures/config/client_secret.json";
/** #var string CLIENT_SECRET */
public const ACCESS_TOKEN = [
"access_token" => "test-data",
"expires_in" => 3592,
"scope" => "https://www.googleapis.com/auth/calendar",
"token_type" => "Bearer",
"created" => 1640858809,
];
...
...
...
}
public function setUp(): void
{
parent::setUp();
$this->googleClientMock = $this->getMockBuilder(Google_Client::class)
->disableOriginalConstructor()
->onlyMethods(
[
"isAccessTokenExpired",
"setAuthConfig",
"getRefreshToken",
"fetchAccessTokenWithRefreshToken",
"fetchAccessTokenWithAuthCode",
"getAccessToken",
"setAccessToken",
]
)
->getMock();
$this->googleServiceCalendarResourceEventsMock = $this->getMockBuilder(Events::class)
->disableOriginalConstructor()
->onlyMethods(["get"])
->getMock();
$this->googleServiceCalendarEventMock = $this->getMockBuilder(Event::class)
->disableOriginalConstructor()
->getMock();
$this->storageInterfaceCacheMock = $this->getMockForAbstractClass(StorageInterface::class);
$this->container->setAllowOverride(true);
$this->container->setService(Google_Client::class, $this->googleClientMock);
$this->container->setService(Events::class, $this->googleServiceCalendarResourceEventsMock);
$this->container->setService(Event::class, $this->googleServiceCalendarEventMock);
$this->container->setService(StorageInterface::class, $this->storageInterfaceCacheMock);
$this->container->setAllowOverride(true);
$this->googleClientMock->method("setAuthConfig")->willReturn(true);
$this->calendarClientService = $this->container->get("ServiceManager")->get(CalendarClientService::class);
$this->calendarClientService->setClientSecretPath(CalendarControllerTest::CLIENT_SECRET);
}
/** #throws ExceptionInterface */
public function testAccessTokenIsExpiredAndFailureToRefreshTokenWillGenerateNewAccessToken()
{
$this->calendarClientService->setAuthCode(CalendarControllerTest::DEFAULT_TESTING_VALUE);
$this->googleClientMock->method("isAccessTokenExpired")->willReturn(true);
$this->googleClientMock->method("getRefreshToken")->willReturn(false);
$this->googleClientMock->method("fetchAccessTokenWithAuthCode")->willReturn(
CalendarControllerTest::ACCESS_TOKEN
);
$this->storageInterfaceCacheMock->method("setItem")->willReturn(true);
$this->googleClientMock->method("setAccessToken")->willReturnSelf();
$this->assertTrue($this->calendarClientService->authenticateClient($this->googleClientMock));
}

problem with a request on 2 tables with symfony

I trie to get the documents stored with a message in the message_document table with a doctrine request but the request loops and ends up filling my memory
I tried the same request with sql on Dbeaver and it runs with no problem
great thanks for your help
My message.php
enter code here<?php
namespace App\Entity;
use DateTime;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\MessageRepository;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass=MessageRepository::class)
*/
class Message
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\ManyToOne(targetEntity=Conversation::class, inversedBy="messages")
*/
private $conversation;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="messages")
*/
private $user;
/**
* #ORM\OneToMany(targetEntity=MessageDocument::class, mappedBy="messages")
*/
private $messageDocuments;
public function __construct( string $content, User $user, Conversation $converstation) {
$this->content = $content;
$this->user = $user;
$this->conversation = $converstation;
$this->createdAt = new \DateTime('now');
$this->messageDocuments = new ArrayCollection();
}
public function getId():?int {
return $this->id;
}
public function getContent():?string {
return $this->content;
}
public function setContent(string $content):self {
$this->content = $content;
return $this;
}
public function getCreatedAt():?\DateTimeInterface {
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt):self {
$this->createdAt = $createdAt;
return $this;
}
public function getConversation():?Conversation
{
return $this->conversation;
}
public function setConversation(?Conversation $conversation):self {
$this->conversation = $conversation;
return $this;
}
public function getUser():?User {
return $this->user;
}
public function setUser(?User $user):self {
$this->user = $user;
return $this;
}
/**
* #return Collection|MessageDocument[]
*/
public function getMessageDocuments(): Collection
{
return $this->messageDocuments;
}
public function addMessageDocument(MessageDocument $messageDocument): self
{
if (!$this->messageDocuments->contains($messageDocument)) {
$this->messageDocuments[] = $messageDocument;
$messageDocument->setMessages($this);
}
return $this;
}
public function removeMessageDocument(MessageDocument $messageDocument): self
{
if ($this->messageDocuments->removeElement($messageDocument)) {
// set the owning side to null (unless already changed)
if ($messageDocument->getMessages() === $this) {
$messageDocument->setMessages(null);
}
}
return $this;
}
}
my MessageDocument.php
enter code here<?php
namespace App\Entity;
use App\Repository\MessageDocumentRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=MessageDocumentRepository::class)
*/
class MessageDocument
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $fileName;
/**
* #ORM\Column(type="datetime")
*/
private $updatedAt;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $type;
/**
* #ORM\ManyToOne(targetEntity=Message::class, inversedBy="messageDocuments")
*/
private $message;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="messageDocuments")
*/
private $sender;
public function getId(): ?int
{
return $this->id;
}
public function getFileName(): ?string
{
return $this->fileName;
}
public function setFileName(string $fileName): self
{
$this->fileName = $fileName;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(?string $type): self
{
$this->type = $type;
return $this;
}
public function getMessage(): ?Message
{
return $this->message;
}
public function setMessage(?Message $message): self
{
$this->message = $message;
return $this;
}
public function getSender(): ?User
{
return $this->sender;
}
public function setSender(?User $sender): self
{
$this->sender = $sender;
return $this;
}
}
the request on MessageDocument join Message
/**
* #return MessageDocument[] Returns an array of MessageDocument objects
*/
//$qb->expr()->eq('md.id = :val')
//,Join::WITH,$qb->expr()->eq('md.id = :val')
public function findDocByMessageId($messageId)
{
return $this->createQueryBuilder('md')
->select('md')
->join('md.message','m')
->where('m.id =:val')
->setParameter('val', $messageId)
->setMaxResults(20)
->getQuery()
->getResult();
}
the calling of the repo request
$allMessages = new ArrayCollection();
$docs=[];
foreach ($messages as $messageUnique) {
$messId = $messageUnique->getId();
$documentsMessages = $messageRepository->findDocByMessageId($messId);
if($documentsMessages !== null){
foreach($documentsMessages as $document){
$docs=$document;
}
//$messageUnique->addMessageDocument($document);
}
$conversation->setLastMessage($messageUnique);
$messageUnique = array(
'id' => $messageUnique->getId(),
'author' => $messageUnique->getUser()->getFullName(),
'authorId' => $messageUnique->getUser()->getId(),
'content' => $messageUnique->getContent(),
'createdAt' => $messageUnique->getCreatedAt()
);
$allMessages->add($messageUnique);
}
I finally solved the problem :)
I have used the #ignore decorator on two properties of the MessageDocument entity
I have changed my code and now error message is about circular reference
Message entity
<?php
namespace App\Entity;
use DateTime;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\MessageRepository;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass=MessageRepository::class)
*/
class Message
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\ManyToOne(targetEntity=Conversation::class, inversedBy="messages")
*/
private $conversation;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="messages")
*/
private $user;
/**
* #ORM\OneToMany(targetEntity=MessageDocument::class, mappedBy="message")
*/
private $messageDocument;
public function __construct( string $content, User $user, Conversation $converstation) {
$this->content = $content;
$this->user = $user;
$this->conversation = $converstation;
$this->createdAt = new \DateTime('now');
$this->messageDocuments = new ArrayCollection();
}
public function getId():?int {
return $this->id;
}
public function getContent():?string {
return $this->content;
}
public function setContent(string $content):self {
$this->content = $content;
return $this;
}
public function getCreatedAt():?\DateTimeInterface {
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt):self {
$this->createdAt = $createdAt;
return $this;
}
public function getConversation():?Conversation
{
return $this->conversation;
}
public function setConversation(?Conversation $conversation):self {
$this->conversation = $conversation;
return $this;
}
public function getUser():?User {
return $this->user;
}
public function setUser(?User $user):self {
$this->user = $user;
return $this;
}
/**
* #return Collection|MessageDocument[]
*/
public function getMessageDocument(): Collection
{
return $this->messageDocument;
}
public function addMessageDocument(MessageDocument $messageDocument): self
{
if (!$this->messageDocument->contains($messageDocument)) {
$this->messageDocument[] = $messageDocument;
$messageDocument->setMessages($this);
}
return $this;
}
public function removeMessageDocument(MessageDocument $messageDocument): self
{
if ($this->messageDocuments->removeElement($messageDocument)) {
// set the owning side to null (unless already changed)
if ($messageDocument->getMessages() === $this) {
$messageDocument->setMessages(null);
}
}
return $this;
}
}
MessageDocument entity
<?php
namespace App\Entity;
use App\Repository\MessageDocumentRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=MessageDocumentRepository::class)
*/
class MessageDocument
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $fileName;
/**
* #ORM\Column(type="datetime")
*/
private $updatedAt;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $type;
/**
* #ORM\ManyToOne(targetEntity=Message::class, inversedBy="messageDocument")
*/
private $message;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="messageDocument")
*/
private $sender;
public function getId(): ?int
{
return $this->id;
}
public function getFileName(): ?string
{
return $this->fileName;
}
public function setFileName(string $fileName): self
{
$this->fileName = $fileName;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(?string $type): self
{
$this->type = $type;
return $this;
}
public function getMessage(): ?Message
{
return $this->message;
}
public function setMessage(?Message $message): self
{
$this->message = $message;
return $this;
}
public function getSender(): ?User
{
return $this->sender;
}
public function setSender(?User $sender): self
{
$this->sender = $sender;
return $this;
}
}
js use to call controller from twig
function getMessages(conversationId , userId) {
superConversationId = conversationId;
userIdEnCours = userId;
//* On vide ce qu'il y avait avant
removeAllChildNodes(document.querySelector('.msg_history'));
//* On remet toutes les conversations en blanc
let allDivs = document.getElementsByClassName('chat_list');
for (let div of allDivs) {
div.style.background = '#f8f8f8';
}
//* background-color light-grey quand conversation selectionné
$("#"+ conversationId).css('background', "#e1e1e1")
let xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
//si requête validé on traite la réponse
if (this.readyState == 4 && this.status == 200) {
let messages = JSON.parse(this.response);
let discussion = document.querySelector('.msg_history');
for (let message of messages) {
let dateMessage = new Date(message.createdAt)
var lastMessageId = message.id;
//* Affichage selon envoyé ou reçu
if (message.authorId == userIdEnCours) {
discussion.innerHTML += "<div class=\"outgoing_msg col-12 \"\><div class=\"sent_msg col-6 m-0\">"
+ "<p class='m-0'>" + message.content + "</p>"
+ "<span class=\"time_date\"> De " + message.author + " | " + dateMessage.toLocaleString() + "</span>"
+ "</div>"
;
} else {
discussion.innerHTML += "<div class=\"incoming_msg col-12 \">"
+ "<div class=\"received_msg col-12\"\><div class=\"received_withd_msg col-6\">"
+ "<p>" + message.content + "</p>"
+ "<span class=\"time_date_receiver\"> De " + message.author + " | " + dateMessage.toLocaleString() + "</span>"
+ "</div></div>"
;
}
}
//* scroll dernier message
let divMessagerie = document.querySelector(".msg_history");
divMessagerie.scrollTop = divMessagerie.scrollHeight;
//vl le 13/09
// ne voyant pas l'utilité ...
/* Interval = setInterval( () => {
$.ajax({
type: "POST",
url : "/checkMessage/" + lastMessageId,
success: function (response) {
if (response === true) {
clearInterval(Interval);
getMessages(
superConversationId,
userIdEnCours
);
}
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert(errorThrown);
}
});
}, 5000); */
}
};
//! Ouverture de la requete (MOCK VERS PROD)
//* PROD
// xmlhttp.open("GET", "http://www.generation-boomerang.com/messagerie/conv/" + conversationId);
//* DEV
xmlhttp.open("GET", "/messagerie/conv/" + conversationId);
xmlhttp.send();
}
The controller to get the messages from a conversation
/**
* #Route("/messagerie/conv/{id}" , name="messagerie_getMessagesOfConv")
* #Security("is_granted('ROLE_ABONNE') or is_granted('ROLE_ADMIN')", message="Merci de vous abonner au portail pour bénéficier de cette super fonctionnalité !")
*/
public function getMessagesOfConv(int $id, EntityManagerInterface $entityManager, ConversationRepository $conversationRepository, ParticipantRepository $participantRepository,MessageDocumentRepository $messageRepository) {
//* Récup du user + check à faire
$userEncours = $this->getUser();
$userId = $userEncours->getId();
//* Ckeck si la conversation appartient bien au user
$check = $participantRepository->checkBelongs($id, $userId);
if ($check != 1) {
return $this->json('cette conv ne te regarde pas !');
}
$conversation = $conversationRepository->find($id);
$messages = $conversation->getMessages();
// $documentsMessages = new ArrayCollection();//vl
$allMessages = new ArrayCollection();
// $docs=;
foreach ($messages as $messageUnique) {
$messId = $messageUnique->getId();
$documentsMessages = $messageRepository->findDocByMessageId($messId);
if($documentsMessages !== null){
foreach($documentsMessages as $document){
// $docs=$document;
$messageUnique->addMessageDocument($document);
}
}
$conversation->setLastMessage($messageUnique);
$messageUnique = array(
'id' => $messageUnique->getId(),
'author' => $messageUnique->getUser()->getFullName(),
'authorId' => $messageUnique->getUser()->getId(),
'content' => $messageUnique->getContent(),
'createdAt' => $messageUnique->getCreatedAt(),
'messageDocuments'=>$messageUnique->getMessageDocument()
);
$allMessages->add($messageUnique);
}
$entityManager->persist($conversation);
$entityManager->flush();
//echo '<pre>'; var_dump( $conversation);exit;echo '</pre>';//
return $this->json($allMessages);
}
conversation entity
<?php
namespace App\Entity;
use App\Repository\ConversationRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ConversationRepository::class)
*/
class Conversation {
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity=Message::class, cascade={"persist", "remove"})
*/
private $lastMessage;
/**
* #ORM\OneToMany(targetEntity=Message::class, mappedBy="conversation")
*/
private $messages;
/**
* #ORM\OneToMany(targetEntity="Participant", mappedBy="conversation")
*/
private $participants;
/**
* #ORM\Column(type="string" , length=50)
*/
private $title;
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
public function __construct() {
$this->participants = new ArrayCollection();
$this->messages = new ArrayCollection();
$this->createdAt = new \DateTime('now');
}
public function getId():?int {
return $this->id;
}
public function getLastMessage():?Message {
return $this->lastMessage;
}
public function setLastMessage(?Message $lastMessage):self {
$this->lastMessage = $lastMessage;
return $this;
}
/**
* #return Collection|Message[]
*/
public function getMessages():Collection {
return $this->messages;
}
public function addMessage(Message $message):self {
if (!$this->messages->contains($message)) {
$this->messages[] = $message;
$message->setConversation($this);
}
return $this;
}
public function removeMessage(Message $message):self {
if ($this->messages->contains($message)) {
$this->messages->removeElement($message);
// set the owning side to null (unless already changed)
if ($message->getConversation() === $this) {
$message->setConversation(null);
}
}
return $this;
}
/**
* #return Collection|Participant[]
*/
public function getParticipants():Collection {
return $this->participants;
}
public function addParticipant(Participant $participant):self {
if (!$this->participants->contains($participant)) {
$this->participants[] = $participant;
$participant->setConversation($this);
}
return $this;
}
public function removeParticipant(Participant $participant):self {
if ($this->participants->contains($participant)) {
$this->participants->removeElement($participant);
// set the owning side to null (unless already changed)
if ($participant->getConversation() === $this) {
$participant->setConversation(null);
}
}
return $this;
}
/**
* Get the value of title
*/
public function getTitle() {
return $this->title;
}
/**
* Set the value of title
* #return self
*/
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function getCreatedAt():?\DateTimeInterface {
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt):self {
$this->createdAt = $createdAt;
return $this;
}
}
and participant entity
<?php
namespace App\Entity;
use App\Repository\ParticipantRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=ParticipantRepository::class)
*/
class Participant
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="participants")
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity="Conversation", inversedBy="participants")
*/
private $conversation;
private $messageReadAt;
/**
* Get the value of id
*/
public function getId()
{
return $this->id;
}
/**
* Get the value of user
*/
public function getUser()
{
return $this->user;
}
/**
* Set the value of user
*
* #return self
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* Get the value of conversation
*/
public function getConversation()
{
return $this->conversation;
}
/**
* Set the value of conversation
*
* #return self
*/
public function setConversation($conversation)
{
$this->conversation = $conversation;
return $this;
}
/**
* Get the value of messageReadAt
*/
public function getMessageReadAt()
{
return $this->messageReadAt;
}
/**
* Set the value of messageReadAt
*
* #return self
*/
public function setMessageReadAt($messageReadAt)
{
$this->messageReadAt = $messageReadAt;
return $this;
}
}
there is also a CreateMessageHandler and GetMessageHanler (but I don't understand how it works)
CreateMessageHandler
<?php
namespace App\MessageHandler;
use App\Entity\User;
use App\Entity\Message;
use App\Entity\Conversation;
use App\Message\CreateMessage;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
class CreateMessageHandler implements MessageHandlerInterface
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager) {
$this->entityManager = $entityManager;
}
public function __invoke(CreateMessage $createMessage)
{
$conversation = $this->entityManager->getRepository(Conversation::class)->find($createMessage->getConversationId());
if(is_null($conversation))
{
$conversation = new Conversation();
$this->entityManager->persist($conversation);
$this->entityManager->flush();
} else {
$message = new Message(
$createMessage->getContent(),
$this->entityManager->getRepository(User::class)->find($createMessage->getUserId()),
$conversation,
);
// if ()
}
// Debug
// echo $createMessage->getContent();
// echo $message->getUser()->getId();
// echo $message->getConversation()->getId();
$this->entityManager->persist($message);
$this->entityManager->flush();
}
}
GetMessageHandler
<?php
namespace App\MessageHandler;
use App\Entity\Message;
use App\Message\GetMessages;
use App\Repository\MessageRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
class GetMessagesHandler implements MessageHandlerInterface
{
private $entityManager;
private $messageRepository;
public function __construct(EntityManagerInterface $entityManager, MessageRepository $messageRepository) {
$this->entityManager = $entityManager;
}
public function __invoke(GetMessages $message)
{
//Récupérer les messages de la conversation
return $this->entityManager->getRepository(Message::class)->findBy(['conversation' => $message->getConversationId()]);
}
}
I think everything is there.
A little long sorry...
Hope somebody could find the reason why I have this circular reference
Thanks
I'm not sure at all about setting $messageUnique = array() with value of the same variable, you should probably use another name of variable. If you have an infinite loop problem it's coming from the PHP in your case.

Symfony serializer and ManyToMany setter

I have two entity.
Book and Author
class Book
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private int $id;
/**
* #ORM\Column(type="string", length=255)
*/
private string $name;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Author", fetch="EXTRA_LAZY")
* #ORM\JoinTable(name="books_authors",
* joinColumns={#ORM\JoinColumn(name="book_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="author_id", referencedColumnName="id")}
* )
* #var PersistentCollection|ArrayCollection
*/
private $authors;
public function __construct()
{
$this->authors = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getAuthors()
{
return $this->authors;
}
public function setAuthors($authors)
{
$this->authors = $authors;
}
public function addAuthor($author){
if(!$this->authors->contains($author))
$this->authors->add($author);
}
public function removeAuthor($author){
if($this->authors->contains($author)){
$this->authors->removeElement($author);
}
}
}
In BookController i try create new book and use Serializer
book body like
{
"name:"book name",
"authors":[
1,
2
]
}
In controller
$serializer = new Serializer([new ObjectNormalizer()], [new JsonEncoder()]);
$book = $this->serializer->deserialize($request->getContent(), Book::class, 'json');
try{
$this->entityManager->persist($book);
$this->entityManager->flush();
} catch (UniqueConstraintViolationException $exception){
return new JsonResponse('already exist', JsonResponse::HTTP_FOUND);
}
but on flush() I have exception:
Expected value of type "App\Entity\Author" for association field "App\Entity\Book#$authors", got "integer" instead.
because book->authors now is an id collection, but entity collection needle.
I have some variants of resolve this, but what is the best way for create and update entity?
You need to create EntityNormalizer like this:
[Medium post][1]
class EntityNormalizer extends ObjectNormalizer implements DenormalizableInterface
{
private $em;
public function __construct(EntityManagerInterface $em, ?ClassMetadataFactoryInterface $classMetadataFactory = null, ?NameConverterInterface $nameConverter = null, ?PropertyAccessorInterface $propertyAccessor = null, ?PropertyTypeExtractorInterface $propertyTypeExtractor = null)
{
$this->em = $em;
parent::__construct($classMetadataFactory, $nameConverter, $propertyAccessor, $propertyTypeExtractor);
}
public function supportsNormalization($data, $format = null)
{
return false;
}
public function supportsDenormalization($data, $type, $format = null)
{
return !$this->em->getMetadataFactory()->isTransient($type) && (is_string($data) || is_numeric($data));
}
public function denormalize($data, $class, $format = null, array $context = [])
{
return $this->em->find($class, $data);
}
}
[1]: https://medium.com/cloudstek/using-the-symfony-serializer-with-doctrine-relations-69ecb17e6ebd

Symfony2 - Functional Testing: container not injected into a Doctrine DataFixture - Call to a member function get() on a non-object

Attempting to build a user data fixture results in
Fatal error: Call to a member function get() on a non-object in
...\Tests\Repository\DataFixtures\LoadAdminUserData.php on line 35
line 35:
$discriminator = $this->container->get('pugx_user.manager.user_discriminator');
Complete fixture (w/ namespace edit)
<?php
//src\Vol\VolBundle\\DataFixtures\ORM\LoadAdminUserData
namespace Vol\VolBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Description of LoadAdminUserData
*
*/
class LoadAdminUserData extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{
/**
* #var ContainerInterface
*/
private $container;
/**
* {#inheritDoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function load(ObjectManager $manager)
{
$discriminator = $this->container->get('pugx_user.manager.user_discriminator');
$discriminator->setClass('Vol\VolBundle\Entity\Admin');
$userManager = $this->container->get('pugx_user_manager');
$admin = $userManager->createUser();
$admin->setUsername('bborko');
$admin->setEmail('bborko#bogus.info');
$admin->setPlainPassword('123456');
$admin->setEnabled(true);
$userManager->updateUser($admin, true);
$this->addReference('test-user', $admin);
}
public function getOrder()
{
return 3;
}
}
Test
<?php
//src\Vol\VolBundle\Tests\Repository
namespace Vol\VolBundle\Tests\Repository;
use Vol\VolBundle\DataFixtures\ORM\DoctrineTestCase;
/**
* Description of AdminUserRepositoryTest
*
* #author George
*/
class AdminUserRepositoryTest extends DoctrineTestCase
{
/**
* Set up repository test
*/
public function setUp()
{
$this->loadFixturesFromDirectory($this->dir);
}
/**
* Test finding all countries ordered
*/
public function testFindAll()
{
$focuses = $this->getRepository()->findAll();
$this->assertCount(1, $focuses, 'Should return 1 admin user');
}
/**
* Returns repository
*
* #return \Vol\VolBundle\Entity\Admin
*/
protected function getRepository()
{
return $this->em->getRepository('\Vol\VolBundle\Entity\Admin');
}
}
class DoctrineTestCase
<?php
//src\Vol\VolBundle\Tests\Repository
namespace Vol\VolBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
* Class DoctrineTestCase
*
* This is the base class to load doctrine fixtures using the symfony configuration
*/
class DoctrineTestCase extends WebTestCase
{
/**
* #var \Symfony\Component\DependencyInjection\Container
*/
protected $container;
/**
* #var \Doctrine\ORM\EntityManager
*/
protected $em;
/**
* #var string
*/
protected $environment = 'test';
/**
* #var bool
*/
protected $debug = true;
/**
* #var string
*/
protected $entityManagerServiceId = 'doctrine.orm.entity_manager';
protected $dir;
/**
* Constructor
*
* #param string|null $name Test name
* #param array $data Test data
* #param string $dataName Data name
*/
public function __construct($name = null, array $data = array(), $dataName = '')
{
parent::__construct($name, $data, $dataName);
if (!static::$kernel) {
static::$kernel = self::createKernel(array(
'environment' => $this->environment,
'debug' => $this->debug
));
static::$kernel->boot();
}
$this->container = static::$kernel->getContainer();
$this->em = $this->getEntityManager();
$this->dir = __DIR__;
}
/**
* Executes fixtures
*
* #param \Doctrine\Common\DataFixtures\Loader $loader
*/
protected function executeFixtures(Loader $loader)
{
$purger = new ORMPurger();
$executor = new ORMExecutor($this->em, $purger);
$executor->execute($loader->getFixtures());
}
/**
* Load and execute fixtures from a directory
*
* #param string $directory
*/
protected function loadFixturesFromDirectory($directory)
{
$loader = new Loader();
$loader->loadFromDirectory($directory);
$this->executeFixtures($loader);
}
/**
* Returns the doctrine orm entity manager
*
* #return object
*/
protected function getEntityManager()
{
return $this->container->get($this->entityManagerServiceId);
}
}
Solution:
You'll have to pass the container to your fixture-loader inside the TestCase yourself.
Just use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader ...
... which extends Doctrine\Common\DataFixtures\Loader and expects the container ...
... as constructor argument.
use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader;
protected function loadFixturesFromDirectory($directory)
{
$loader = new ContainerAwareLoader($this->container);
$loader->loadFromDirectory($directory);
$this->executeFixtures($loader);
}
When does symfony2 inject the container automatically?
The FrameworkBundle injects the container into instances of ContainerAwareInterface being controllers or commands only.
The corresponding code can be found here and here.
DataFixtures can be container-aware, too.
DoctrineFixtureBundle's Command searches for (container-aware) fixtures and injects the container.
Only fixtures that conventially live in a bundle's DataFixtures/ORM folder are processed.
A quick look at the code reveals it:
foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) {
$paths[] = $bundle->getPath().'/DataFixtures/ORM';
}
The --fixtures flag:
Appending the --fixtures flag - which can be a string or an array - to the doctrine:fixtures:load allows to add additional fixture-paths for processing.

Resources