Weird Doctrine ODM exception when using references together with inheritance - symfony

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

Related

Symfony 4 with Doctrine - save ManyToOne

I have problem with my symfony code:
My repository update method
/**
* #param MenuModel $menu
* #return MenuEntity|MenuModel
* #throws RepositoryException
*/
public function updateMenu(MenuModel $menu)
{
try {
$transformedMenu = $this->menuTransformer->transform($menu);
$transformedMenu = $this->getEntityManager()->merge($transformedMenu);
$this->getEntityManager()->flush($transformedMenu);
$this->getEntityManager()->detach($transformedMenu);
return $this->menuTransformer->transform($transformedMenu);
} catch (\Exception $e) {
throw new RepositoryException($e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
}
}
My Element entity:
/**
* Element.
*
* #ORM\HasLifecycleCallbacks
* #ORM\Table(name="element")
* #ORM\ChangeTrackingPolicy("DEFERRED_EXPLICIT")
* #ORM\Entity(repositoryClass="CP\API\Repository\ElementRepository")
*/
class Element extends \CP\RestBundle\Model\Element
{
use Traits\SystemObjectTrait;
use Traits\ChannelsTrait;
use Traits\PropertiesTrait;
use Traits\WorkflowTrait;
/**
* #ORM\ManyToOne(targetEntity="Menu", inversedBy="contents")
* #ORM\JoinColumn(name="menu_id", referencedColumnName="id")
*/
protected $menu;
/**
* Element constructor.
*/
public function __construct()
{
parent::__construct();
}
/**
* #param int $id
*
* #return Element
*/
public function setId(int $id): self
{
$this->id = $id;
return $this;
}
/**
* Update publication by modification when empty
* Update status according to publication, unpublication and archiving
*
* #ORM\PrePersist()
*/
protected function prePersist()
{
$this->updatePublication();
$this->updateStatus();
}
/**
* Update publication by modification when empty
* Update status according to publication, unpublication and archiving
*
* #ORM\PreUpdate()
*/
public function preUpdate()
{
$this->updatePublication();
$this->updateStatus();
}
/**
* Increases object version
*/
public function increaseVersion()
{
++$this->version;
}
/**
* #return mixed
*/
public function getMenu()
{
return $this->menu;
}
/**
* #param mixed $menu
*/
public function setMenu($menu): void
{
$this->menu = $menu;
}
}
My Menu entity
<?php
namespace CP\API\Entity;
use CP\API\Entity\Traits\TimestampableTrait;
use CP\Model\Configuration;
use CP\Model\Content;
use CP\Model\Language;
use CP\Model\MenuTranslation;
use CP\RestBundle\Model\Locator;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\HasLifecycleCallbacks()
* #ORM\Table(name="custom_menu")
* #ORM\Entity(repositoryClass="CP\API\Repository\MenuRepository")
*/
class Menu
{
use TimestampableTrait;
/**
* #var int
*
* #ORM\Id()
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="CP\API\Entity\Element",mappedBy="menu",cascade={"persist"})
*/
protected $contents;
public function __construct(Menu $parent = null)
{
$dateTime = new \DateTime();
$this->creation = $dateTime;
$this->modification === null && $this->setModification($dateTime);
$this->contents = new ArrayCollection();
}
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* #return ArrayCollection
*/
public function getContents(): ArrayCollection
{
return $this->contents;
}
/**
* #param ArrayCollection $contents
*/
public function setContents(ArrayCollection $contents): void
{
$this->contents = $contents;
}
/**
* #param Element $element
* #return $this
*/
public function addContent(Element $element): self
{
if (!$this->contents->contains($element)) {
$this->contents[] = $element;
$element->setMenu($this);
}
return $this;
}
/**
* #param Element $element
* #return $this
*/
public function removeContent(Element $element): self
{
if ($this->contents->contains($element)) {
$this->contents->removeElement($element);
if ($element->getMenu() === $this) {
$element->setMenu(null);
}
}
return $this;
}
}
My menu model
<?php
namespace CP\Model;
use CP\RestBundle\Model\Element;
use CP\RestBundle\Model\Locator;
use CP\RestBundle\Model\Product;
use CP\RestBundle\Model\Traits\TimestampableTrait;
class Menu extends BaseModel
{
/** #var Content[] */
protected $content;
/**
* #return array|null
*/
public function getContent(): ?array
{
return $this->content;
}
/**
* #param Content[] $content
*/
public function setContent(array $content): void
{
$this->content = $content;
}
}
My transformer from model to entity
public function transform($object)
{
if ($object instanceof Menu) {
$menuData = new MenuEntity();
if ($object->getId())
$menuData->setId($object->getId());
if ($object->getContent() instanceof Content) {
$contentEntity = new ContentEntity();
$content = $object->getContent();
$contentEntity->setId($content->getId());
$menuData->addContent($this->attachToEntityManager($contentEntity));
}
return $menuData;
}
private function attachToEntityManager($object)
{
try {
$attachedObject = $this->entityManager->merge($object);
return $attachedObject;
} catch (ORMException $e) {
throw new ModelTransformationException(sprintf('Model transformation error, object could not be attached to Entity Manager: %s in %s',
$e->getMessage(), $e->getFile() . ':' . $e->getLine()));
}
}
When i try saved data then i don't have any error, but on database nothing change i.e. element entity don't have assign menu_id from relation. I don't know what is wrong, maybe i don't know how to use and save OneToMany relation.
Any idea?
Can you give us more context of what your code is doing ? The constructor of MenuEntity is strange, no use of $parent, a boolean comparaison isn't used. Why do you need to use detach and merge methods of entityManager
- Mcsky
Is right, I don't see the point of using detach and/or merge here.
Normally with a OneToMany relationship you use entity manager and persist both relations, flush and you should have an entity with a OneToMany relationship.
I hope this might be helpful, specifically this part: https://symfony.com/doc/current/doctrine/associations.html#saving-related-entities
example:
// relates this product to the category
$product->setCategory($category);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($category);
$entityManager->persist($product);
$entityManager->flush();

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.

Every parent controller must have `get{SINGULAR}Action($id)` method when i have multi level sub resource in FOS Rest Bundle

I have three controller named BlogController, PostController, CommentController that CommentController is sub resource of PostController and PostController sub resource of BlogController.
/**
* #Rest\RouteResource("blog", pluralize=false)
*/
class BlogController extends FOSRestController
{
public function getAction($blogUri)
{
...
}
}
/**
* #Rest\RouteResource("post", pluralize=false)
*/
class PostController extends FOSRestController
{
public function getAction($postId)
{
...
}
}
/**
* #Rest\RouteResource("comment", pluralize=false)
*/
class CommentController extends FOSRestController
{
public function getAction($commentId)
{
...
}
}
routing.yml
mgh_blog:
resource: MGH\BlogBundle\Controller\BlogController
type: rest
mgh_blog_post:
resource: MGH\BlogBundle\Controller\PostController
type: rest
parent: mgh_blog
mgh_blog_post_comment:
resource: MGH\PostBundle\Controller\CommentController
type: rest
parent: mgh_blog_post
I define getAction methods, but i get following error:
[InvalidArgumentException]
Every parent controller must have `get{SINGULAR}Action($id)` method
where {SINGULAR} is a singular form of associated object
Edit:
I also try to change the method's name to getCommentAction($commentId), getPostAction($postId) and getBlogAction, but it no work.
When I use #RouteResource annotations, method name must be getAction($id), otherwise it doesn't work.
When I change parent of mgh_blog_post_comment router to mgh_blog, it's working!
That error description is awful and a big time waster because it doesn't tell you what the real problem is. Try the following:
/**
* #Rest\RouteResource("blog", pluralize=false)
*/
class BlogController extends FOSRestController
{
public function getAction($blogUri)
{
...
}
}
/**
* #Rest\RouteResource("post", pluralize=false)
*/
class PostController extends FOSRestController
{
public function getAction($blogUri, $postId)
{
...
}
}
/**
* #Rest\RouteResource("comment", pluralize=false)
*/
class CommentController extends FOSRestController
{
public function getAction($blogUri, $postId, $commentId)
{
...
}
}
You didn't have the correct number of arguments in the descendant controller actions. It took me two days of step debugging to figure this out.
The parent route, Blog, looks like:
/blog/{blogUri}
It will match
public function getAction($blogUri)
The child route, Post, looks like:
/blog/{blogUri}/post/{postId}
It will not match the code below because it needs two parameters. The same is true for the grandchild--which is looking for three parameters:
public function getAction($postId)
The grandchild route, Comment, looks like:
/blog/{blogUri}/post/{postId}/comment/{commentId}
The code keeps track of the ancestors of each controller.
The post has 1 ancestor. When building the routes for the post controller, the code looks at the number of parameters on the 'get action'. It take the number of parameters and subtracts the number of ancestors. If the difference is not equal to one, it throws the error.
Conclusion, for each descendant, it needs to include the ID parameters of it ancestors AND its own ID. There should always be one more parameter than there are ancestors.
Have you tried?:
class CommentController extends FOSRestController
{
public function getCommentAction($commentId)
{
...
}
}
Try:
public function cgetAction(Request $request)
{
...
}
This is my controller example:
<?php
namespace Cf\SClinicBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\Annotations\RouteResource;
use Cf\SClinicBundle\Entity\CfAcquireImage;
use Doctrine\DBAL\DBALException as DBALException;
use Doctrine\ORM\NoResultException as NoResultException;
/**
* CfAcquireImage controller.
*
* #RouteResource("acquire-image")
*/
class ApiCfAcquireImageController extends FOSRestController
{
/**
* #var array
*/
public $status;
/**
* #var
*/
public $parameter;
/**
* #var
*/
private $role_name;
/**
* Constructor
*/
public function __construct()
{
}
/**
* Lists all Cf Acquire Image entities.
*
* #param Request $request
*
* #return mixed
*/
public function cgetAction(Request $request)
{
}
/**
* Finds a Cf Acquire Image entity by id.
*
* #param Request $request
* #param $id $id
*
* #return array
*/
public function getAction(Request $request, $id)
{
}
/**
* Create a new Cf Acquire Image entity.
*
* #param Request $request
*
* #return mixed
*/
public function postAction(Request $request)
{
}
/**
* #param Request $request
* #param $id
*
* #return array
*/
public function putAction(Request $request, $id)
{
}
/**
* Deletes a Cf Acquire Image entity.
*
* #param Request $request
* #param $id
*
* #return mixed
*/
public function deleteAction(Request $request, $id)
{
}
}

How to extend the Page class from Symfony simple cms bundle?

I'm trying to extend the default Page class from symfony simple cms bundle.
The problem:
The custom property is not persisted.
Below is the code of the class which extends from BasePage.
use Doctrine\ODM\PHPCR\Mapping\Annotations\Document;
use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRODM;
use Symfony\Cmf\Bundle\SimpleCmsBundle\Doctrine\Phpcr\Page as BasePage;
/**
* {#inheritDoc}
* #PHPCRODM\Document(referenceable=true)
*/
class Product extends BasePage
{
public $node;
/**
* #var string(nullable=true)
*/
private $code;
/**
* Get Code
* #return string
*/
public function getCode()
{
return $this->code;
}
/**
* Set code
* #return Product
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
}
This looks almost correct, but you miss a mapping on the $code:
/**
* #PHPCRODM\String(nullable=true)
*/
private $code;
I assume that $code is not language dependant. Otherwise you would need nullable=true,translatable=true
If you also want to have the PHPCR node mapped, you need
/**
* #PHPCRODM\Node
*/
public $node;

Getters and setters using Doctrine 2

I have an Entity i.e Users. I want to make getters and setters of this entity in Doctrine, so that Doctrine can read it.
How can I do it, can someone provide me basic example? I am a beginner
How to insert data in this database table?
Here is my Users entity
<?php
/**
* #Entity
* #Table(name="users")
* Total Number of Columns : 32
*/
class Users{
/* Attributes of Users */
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
* #dummy
* #Assert\NotEmpty
*/
private $id;
/**
* #Column(type="string")
* #Assert\NotEmpty
*/
private $name;
/**
* #Column(type="string")
* #Assert\NotEmpty
*/
private $email;
}
?>
Try with this command:
php app/console doctrine:generate:entities YourBundle:YourEntity
For example, if you wanted to have a setter for your email property, you would do:
public function setEmail($email)
{
$this->email = $email;
return $this;
}
public function getEmail()
{
return $this->email;
}
The first is the setter (it sets the value of email on the object) and the second is the getter (it gets the value of email from the object). Hope that helps :)
You can use magic methods if you're lazy enough not to define your own methods for each property.
public function __get($property)
{
return $this->$property;
}
public function __set($property,$value)
{
$this->$property = $value;
}
It's better to create a method for each property though
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
Have a look at the answers here Doctrine 2 Whats the Recommended Way to Access Properties?

Resources