How to use Symfony EasyAdmin with entities having a constructor? - symfony

Does EasyAdmin support entity classes with constructor arguments for properties that are meant to be not nullable? EasyAdmin instantiates the entity class even if you click the "Add " button, right? Unfortunatelly this results in an "Too few arguments to function __construct()" error. Do you have a solution for this problem?
I tend to use the constructor for entity properties that are not nullable. Unfortunatelly EasyAdmin throws errors like this one when I click on the e.g. Add FiscalYear button to create a new entity object (FiscalYear in my example):
Too few arguments to function App\Entity\FiscalYear::__construct(), 0 passed in /myProject/vendor/easycorp/easyadmin-bundle/src/Controller/AdminControllerTrait.php on line 618 and exactly 2 expected
How can I prevent these errors? As you can see in the following entity class the two constructor arguments represent the data that is meant to be submitted via the form:
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\FiscalYearRepository")
*/
class FiscalYear
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* #ORM\Column(type="integer")
*/
private int $title;
/**
* #ORM\Column(type="boolean", options={"default": 0})
*/
private bool $completed = false;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Company", inversedBy="fiscalYears")
* #ORM\JoinColumn(nullable=false)
*/
private Company $company;
public function __construct(int $title, Company $company)
{
$this->title = $title;
$this->company = $company;
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): int
{
return $this->title;
}
public function setTitle(int $title): void
{
$this->title = $title;
}
public function getCompleted(): bool
{
return $this->completed;
}
public function setCompleted(bool $completed): void
{
$this->completed = $completed;
}
public function getCompany(): Company
{
return $this->company;
}
public function setCompany(Company $company): void
{
$this->company = $company;
}
}
Is there a possibility to let EasyAdmin show the "create a new entity object" form without instantiating the entity class?

No, EasyAdmin doesn't natively support constructor with argument.
To avoid this problem, you have three solution.
solution1: Override EasyAdminController
The documentation explains this method.
// src/Controller/AdminController.php
namespace App\Controller;
use EasyCorp\Bundle\EasyAdminBundle\Controller\EasyAdminController;
class FiscalYearController extends EasyAdminController
{
public function createNewFiscalYearEntity()
{
//your own logic here to retrieve title and company
return new FiscalYear($title, $company);
}
}
Depending you business model, it could be very difficult to retrieve title and company
solution2: Respect the entity pattern and help your business model with a factory pattern
Your entities should respect the entity pattern and their constructor should be edited to remove arguments.
To replace your constructor in your business model, create a factory.
class FiscalYearFactory
{
public static function create(int $title, Company $company): FiscalYear
{
$fiscalYear = new FiscalYear();
$fiscalYear->setCompany($company);
$fiscalYear->setTitle($title);
return $fiscalYear;
}
}
in your model, you have to do some updates:
//Comment code like this in your business model
$fiscalYear = new FiscalYear(2020,$company);
//Replace it, by this code:
$fiscalYear = FiscalYearFactory::create(2020,$company);
Solution3 Accept null values in your constructor.
I do NOT like this solution. Your properties shall be edited too to accept null values, your getters shall be edited to return null value. This is a solution, but I discourage you to use it.
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\FiscalYearRepository")
*/
class FiscalYear
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private ?int $id = null;
/**
* #ORM\Column(type="integer")
*/
private ?int $title;
/**
* #ORM\Column(type="boolean", options={"default": 0})
*/
private bool $completed = false;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Company", inversedBy="fiscalYears")
* #ORM\JoinColumn(nullable=false)
*/
private Company $company;
public function __construct(?int $title = null, ?Company $company = null)
{
$this->title = $title;
$this->company = $company;
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?int
{
return $this->title;
}
You should use the first solution which is a better practice

Related

Symfony 5: How to get isVerified User in authenticator

i'm still a beginner in symfony so i hope my question will be fastly answered. I did a lot of research on internet without finding anything about my problem.
I use Symfony 5.3 basic authenticator which is generated when we do the bin/console make:auth with maker bundle. All basic config, i didn't touch anything in security.yaml. The thing i want to perform is set a authentication error if the user is not verified by Email. My User entity is very basic too with make:user. But it seems like the User representation we get with this token in onAuthenticationSuccess doesn't contain any "isVerified" method, an interesting fact is that he doesn't contain any "getEmail" method too, seems like he just contain the main accessors (username, password). Any solution ?
(Login is actually working, i just can't perform this verifying trick.)
EDIT: Just saw that "isVerifed" and "getEmail" methods can't be called from any $this->getUser() in controllers too. But when we do a new User(), we can call these methods.
EDIT AGAIN: Ok it's because $this->getUser() and $token->getUser() extends UserInterface and there's not any getEmail() or isVerified() method in this interface. So what is the trick ? I keep investigating.
Authenticator:
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class Authenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
private UrlGeneratorInterface $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function authenticate(Request $request): PassportInterface
{
$parameters = json_decode($request->getContent(), true);
return new Passport(
new UserBadge($parameters['username']),
new PasswordCredentials($parameters['password'])
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if (!$token->getUser()->isVerified()) { //isVerified show "undefined method"
$request->getSession()->set(Security::AUTHENTICATION_ERROR, "You are not verified. Check your emails.");
}
return null;
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}
My User:
<?php
namespace App\Entity;
use App\Repository\UserSymfangularRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass=UserSymfangularRepository::class)
* #UniqueEntity(fields={"username"}, message="There is already an account with this username")
*/
class UserSymfangular implements UserInterface, PasswordAuthenticatedUserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $username;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
/**
* #ORM\Column(type="string", length=255)
*/
private $email;
/**
* #ORM\Column(type="boolean")
*/
private $isVerified = false;
public function getId(): ?int
{
return $this->id;
}
/**
* #deprecated since Symfony 5.3, use getUserIdentifier instead
*/
public function getUsername(): string
{
return (string) $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->username;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* Returning a salt is only needed, if you are not using a modern
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
*
* #see UserInterface
*/
public function getSalt(): ?string
{
return null;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getIsVerified(): ?bool
{
return $this->isVerified;
}
public function setIsVerified(bool $isVerified): self
{
$this->isVerified = $isVerified;
return $this;
}
public function isVerified(): bool
{
return $this->isVerified;
}
}
EDIT: Solved it. Actually i can do $this->getUser()->getEmail() or $this->getUser()->isVerified(), but the methods just doesn't appear in the autocomplete of my VSCode. Sorry for all of this.

How to create an entity with several dates in a single form with EasyAdmin

I am trying to develop my first symfony web-app and I have decided to use the bundle EasyAdmin.
In this web-app, I would like to define the following model : an Event with several dates.
In order to create this, I have create 2 entities with the help of the symfony console : Event and EventDate:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\EventRepository")
*/
class Event
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\EventDate", mappedBy="event", orphanRemoval=true)
*/
private $dates;
public function __construct()
{
$this->dates = 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;
}
/**
* #return Collection|EventDate[]
*/
public function getDates(): Collection
{
return $this->dates;
}
public function addDate(EventDate $date): self
{
if (!$this->dates->contains($date)) {
$this->dates[] = $date;
$date->setEvent($this);
}
return $this;
}
public function removeDate(EventDate $date): self
{
if ($this->dates->contains($date)) {
$this->dates->removeElement($date);
// set the owning side to null (unless already changed)
if ($date->getEvent() === $this) {
$date->setEvent(null);
}
}
return $this;
}
public function __toString(): String
{
return $this->name;
}
}
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\EventDateRepository")
*/
class EventDate
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="date")
*/
private $date;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Event", inversedBy="dates")
* #ORM\JoinColumn(nullable=false)
*/
private $event;
public function getId(): ?int
{
return $this->id;
}
public function getDate(): ?\DateTimeInterface
{
return $this->date;
}
public function setDate(\DateTimeInterface $date): self
{
$this->date = $date;
return $this;
}
public function getEvent(): ?Event
{
return $this->event;
}
public function setEvent(?Event $event): self
{
$this->event = $event;
return $this;
}
}
In order to be user-friendly, I would like to "customize" the form of an Event in order to allow the user to create in the same form the event and its dates.
In order to do this, I have define the Event entity's form like that:
easy_admin:
entities:
Event:
class: App\Entity\Event
form:
fields:
- {property: 'name'}
- {property: 'dates', type: 'collection'}
The render of the collection is right because I can add or remove a date:
But As you can see, the field that represent the EventDate is an edit text. I think it's because the field represent the EventDate class and not only the date attribute.
The aim is to have the date selector that I have if I add a new EventDate in EasyAdmin:
So the question is: How to custom EasyAdmin in order to add an Event and its dates in a single form?
Thank you for your help!
I found the way to do it.
I need to modify my yaml EasyAdmin file in order to introduce an entry_type:
easy_admin:
entities:
Event:
class: App\Entity\Event
form:
fields:
- {property: 'name'}
- {property: 'dates', type: 'collection', type_options: {entry_type: 'App\Form\EventDateForm', by_reference: false}}
Then, I have to create the EventDateForm class:
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class EventDateForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('date');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'App\Entity\EventDate'
));
}
}
I also need to update the $date attribut of my Event entity like this:
/**
* #ORM\OneToMany(targetEntity="App\Entity\EventDate", mappedBy="event", orphanRemoval=true, cascade={"persist", "remove"})
*/
private $dates;
The render is not very beautiful but it works:

Embedding relations with api platform

My doubt is about Api Platform. (https://api-platform.com)
I have two entities. Question and Answer. And I want to have a POST call to create a question with one answer. I show my entities.
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ApiResource(
* normalizationContext={"groups"={"question"}},
* denormalizationContext={"groups"={"question"}})
* #ORM\Entity
*/
class Question
{
/**
* #Groups({"question"})
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #Groups({"question"})
* #ORM\Column
* #Assert\NotBlank
*/
public $name = '';
/**
* #Groups({"question"})
* #ORM\OneToMany(targetEntity="Answer", mappedBy="question", cascade={"persist"})
*/
private $answers;
public function getAnswers()
{
return $this->answers;
}
public function setAnswers($answers): void
{
$this->answers = $answers;
}
public function __construct() {
$this->answers = new ArrayCollection();
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getId(): int
{
return $this->id;
}
}
And a Answer Entity
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;
/**
*
* #ApiResource
* #ORM\Entity
*/
class Answer
{
/**
* #Groups({"question"})
* #ORM\Id
* #ORM\Column(type="guid")
*/
public $id;
/**
* #Groups({"question"})
* #ORM\Column
* #Assert\NotBlank
*/
public $name = '';
/**
* #ORM\ManyToOne(targetEntity="Question", inversedBy="answers")
* #ORM\JoinColumn(name="question_id", referencedColumnName="id")
*/
public $question;
public function getQuestion()
{
return $this->question;
}
public function setQuestion($question): void
{
$this->question = $question;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getId(): string
{
return $this->id;
}
public function __toString()
{
return $this->getName();
}
}
Now I can create from dashboard of nelmio a question and into an answer. But in database, my answer doesnt have saved the relation with question.
{
"name": "my new question number 1",
"answers": [
{
"id": "ddb66b71-5523-4158-9aa3-2691cae9d473",
"name": "my answer 1 to question number 1"
}
]
}
And other question is... I've changed my id of answer by a guid, because I get and error when I create and answer into question without id. Can I create a question, and answers without to specify an id ?
Thanks in advance
Well, I see some errors in your entities... first since the relation between Question and Answer entity is a OneToMany, your Qestion entity should have this implemented, the:
use ApiPlatform\Core\Annotation\ApiProperty;
//..... the rest of your code
/**
* #ApiProperty(
* readableLink=true
* writableLink=true
* )
* #Groups({"question"})
* #ORM\OneToMany(targetEntity="Answer", mappedBy="question", cascade={"persist"})
*/
private $answers;
public function __construct()
{
//....
$this->answers = new ArrayCollection();
//...
}
public function addAnswer(Answer $answer): self
{
if (!$this->answers->contains($answer)) {
$this->answers[] = $answer;
$answer->setQuestion($this)
}
return $this;
}
public function removeAnswer(Answer $answer): self
{
if ($this->answers->contains($answer)) {
$this->answers->removeElement($answer);
}
return $this;
}
the command
PHP bin/console make:entity
allows you to create a field in your entity of type relation and it creates these methods for you just follow the instructions (after both entities are created, use the command for update Question entity...)
the readableLink ApiProperty annotation is for see embedded object on GET request, is the same if you use serialization groups, if you set it on false then the response will look like this:
{
"name": "my new question number 1",
"answers": [
"/api/answers/1",
"/api/answers/2",
....
]
}
it is used to make responses smaller (among other things)... and the writableLink is for allowing POST request like this (see this example for more info here):
{
"name": "my new question number 1",
"answers": [
{
"id": "ddb66b71-5523-4158-9aa3-2691cae9d473",
"name": "my answer 1 to question number 1"
}
]
}
of course, using the corresponding serialization groups in each entity...
in ApiPlatform the embedded objects are persisted through setters and getters method but also add and remove methods for OneToMany relations, and the ORM does the rest of the work.
let me know if this helps. Cheers!
For the first point, it should persist in database without problem. Anyway, you could create a PostValidateSubscriber for Question entity and check if the relation is there.
<?php /** #noinspection PhpUnhandledExceptionInspection */
namespace App\EventSubscriber;
use ApiPlatform\Core\EventListener\EventPriorities;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
final class QuestionPostValidateSubscriber implements EventSubscriberInterface
{
private $tokenStorage;
public function __construct(
TokenStorageInterface $tokenStorage
) {
$this->tokenStorage = $tokenStorage;
}
/**
* {#inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['checkQuestionData', EventPriorities::POST_VALIDATE]
];
}
/**
* #param GetResponseForControllerResultEvent $event
*/
public function checkQuestionData(GetResponseForControllerResultEvent $event)
{
$bid = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if (!$question instanceof Question || (Request::METHOD_POST !== $method && Request::METHOD_PUT !== $method))
return;
$currentUser = $this->tokenStorage->getToken()->getUser();
if (!$currentUser instanceof User)
return;
}
}
And do an echo or use xdebug for check question.
For the second point, you can add these annotations for the id of entities, so the id's will generate theirself.
#ORM\GeneratedValue()
#ORM\Column(type="integer")

Symfony4 - Undefined index: on entity join

I'm getting an
undefined index: order
error even though from what I can tell using this documentation I'm doing everything right. Any ideas?
OrdersRepository
public function findByDateAndEmployee(\DateTime $date, int $employeeId)
{
return $this->createQueryBuilder('o')
->select('o')
->join('o.employees' ,'e')
->where('e.id = :id')
->setParameter('id',$employeeId)
->getQuery()->getSql();
}
Orders Entity
class Orders
{
...
/**
* One Order has Many Employees.
* #ORM\OneToMany(targetEntity="Employee", mappedBy="order", cascade={"persist", "remove"}, orphanRemoval=TRUE)
*/
private $employees;
I know if I change the variable in "mappedBy" it will change the error message to whatever the variable name is, however when you see below "order" should be the mappedBy variable (at least, I think).
OrderEmployees Entity
class OrderEmployees
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="integer")
*/
private $orderId;
/**
* #ORM\Column(type="smallint")
*/
private $employeeId;
/**
* #ORM\ManyToOne(targetEntity="Orders", inversedBy="employees")
* #ORM\JoinColumn(name="order_id", referencedColumnName="id")
*/
private $order;
public function getId(): ?int
{
return $this->id;
}
public function getOrderId(): ?int
{
return $this->orderId;
}
public function setOrderId(int $orderId): self
{
$this->orderId = $orderId;
return $this;
}
public function getEmployeeId(): ?int
{
return $this->employeeId;
}
public function setEmployeeId(int $employeeId): self
{
$this->employeeId = $employeeId;
return $this;
}
public function getOrder(): Orders
{
return $this->order;
}
public function setOrder(Orders $order): self
{
$this->order = $order;
return $this;
}
}
Changing targetEntity="Employee" to targetEntity="OrderEmployees" in your Orders entity should help.
From the docs:
targetEntity: FQCN of the referenced target entity. Can be the
unqualified class name if both classes are in the same namespace.

Weird Doctrine ODM exception when using references together with inheritance

I've got three classes. The File-Class has a reference to Foobar and Game inherits from Foobar. There are some other Classes which also inherit from Foobar but i left them out as they aren't relevant here. I also left out some unrelevant fields and their getters and setters.
The plan is that every Game has two images, the mainImage and the secondaryImage. I've put those fields into a seperate class from which Game inherits because i need them for a few other classes too.
My problem is that if I load the games from the database as soon as i try to iterate over them I get the following exception:
Notice: Undefined index: in C:\xampp\htdocs\Symfony\vendor\doctrine\mongodb-odm\lib\Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo.php line 1293
For reference here are the lines of ClassMetadataInfo.php
public function getPHPIdentifierValue($id)
{
$idType = $this->fieldMappings[$this->identifier]['type'];
return Type::getType($idType)->convertToPHPValue($id);
}
Here are my classes
File-Class:
namespace Project\MainBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class File
{
/**
* #MongoDB\Id(strategy="INCREMENT")
*/
protected $id;
/**
* #MongoDB\ReferenceOne(targetDocument="Foobar", inversedBy="mainImage")
*/
private $mainImage;
/**
* #MongoDB\ReferenceOne(targetDocument="Foobar", inversedBy="secondaryImage")
*/
private $secondaryImage;
/**
* Get id
*/
public function getId()
{
return $this->id;
}
public function setMainImage($mainImage)
{
$this->mainImage = $mainImage;
return $this;
}
public function getMainImage()
{
return $this->mainImage;
}
public function setSecondaryImage($secondaryImage)
{
$this->secondaryImage = $secondaryImage;
return $this;
}
public function getSecondaryImage()
{
return $this->secondaryImage;
}
}
Foobar-Class:
namespace Project\MainBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\MappedSuperclass
*/
abstract class Foobar
{
/**
* #MongoDB\Id(strategy="INCREMENT")
*/
protected $id;
/**
* #MongoDB\ReferenceOne(targetDocument="File", mappedBy="mainImage")
*/
protected $mainImage;
/**
* #MongoDB\ReferenceOne(targetDocument="File", mappedBy="secondaryImage")
*/
protected $secondaryImage;
/**
* Get id
*/
public function getId()
{
return $this->id;
}
/**
* Set mainImage
*/
public function setMainImage($file)
{
$file->setMainImage($this);
$this->mainImage = $file;
return $this;
}
/**
* Get mainImage
*/
public function getMainImage()
{
return $this->mainImage;
}
/**
* Set secondaryImage
*/
public function setSecondaryImage($file)
{
$file->setSecondaryImage($this);
$this->secondaryImage = $file;
return $this;
}
/**
* Get secondaryImage
*/
public function getSecondaryImage()
{
return $this->secondaryImage;
}
}
Game-Class:
namespace Project\MainBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class Game extends Foobar
{
/**
* #MongoDB\String
*/
private $name;
/**
* Set name
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*/
public function getName()
{
return $this->name;
}
}
Though it doesn't really matter but here is my function i want to execute:
$dm = $this->get('doctrine_mongodb')->getManager();
$games_all = $dm->getRepository("ProjectMainBundle:Game")->createQueryBuilder()->sort('id', 'ASC')->getQuery()->execute();
foreach ($games_all as $singlegame) { // it breaks here
// Here i would do stuff
}
Is this a bug in Doctrine ODM or am I doing something wrong? Are the classes correct? I have tried everything but it just wont work.
I think it is too late for your question, but maybe there are other users having the same problem (as me).
The problem is related to Foobar being a MappedSuperclass. Had the same problem as described by you and at https://github.com/doctrine/mongodb-odm/issues/241.
Solution is to not reference the abstract class Foobar (=MappedSuperclass) but a concrete implementation (=Document) - as in your case - Game.
See also Doctrine ODM returns proxy object for base class instead of sub-classed document

Resources