Symfony 4 MongoDB ODM One to ONe relationship is not working - mongodb

I am facing an error with symfony and mongoDB odm on one to one relationship
for example i have a user that has Work .
User Class:
/**
* #MongoDB\Document
* #MongoDBUnique(fields="email")
*/
class User implements UserInterface
{
/**
* #MongoDB\Id
*/
private $id;
/**
* #MongoDB\Field(type="string")
*/
private $firstName;
/**
* #MongoDB\Field(type="string")
*/
private $lastName;
/**
* #MongoDB\Field(type="string")
* #Assert\NotBlank()
* #Assert\Email()
*/
private $email;
/**
* #MongoDB\ReferenceOne(targetDocument=Work::class)
*/
private $work;
//getter setter
Work Class:
class Work
{
/**
* #MongoDB\Id()
*/
private $id;
/**
* #MongoDB\ReferenceOne(targetDocument=User::class)
*/
private $user;
//getter setter
}
Controller:
class TestingController extends AbstractController
{
/**
* #Route("/testing", name="testing")
* #param DocumentManager $documentManager
* #return Response
* #throws MongoDBException
*/
public function index(DocumentManager $documentManager)
{
$user = new User();
$user->setFirstName('test1');
$user->setLastName('test2');
$user->setEmail('test123#gmail.com');
$documentManager->persist($user);
$work= new Work();
$work->setUser($user);
$documentManager->persist($work);
$documentManager->flush();
return new Response("test");
}
/**
* #Route("/test", name="test")
* #param DocumentManager $documentManager
* #return Response
*/
public function test(DocumentManager $documentManager){
$user = $documentManager->getRepository(User::class)->findAll();
dump($user);
return new Response("test test");
}
}
So I created 2 classes one as user that has one work, I created the user , then I created a work and i assigned the user from the work class.
in MongoDB compass I got under Work collection a reference for the user.
now in the test method in the controller i try to find the users and dump the data.
The problem is whenever i want to find $user->getWork() i get a null value, while the user exists. but the inverse is working fine . whenever i try $work->getUser() i can find the user.
is there anything wrong in my code ? I want to use both methods : $user->getWork() and $work->getUser(),
I have tried adding to the ReferenceOne mappedBy and inversedBy but its always one of the two methods returns null value.

I think you forgot the mappedBy and inversedBy arguments in the annotation. See the documentation: https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/2.1/reference/bidirectional-references.html#one-to-one

Related

Get the id, not the object in a ManyToOne relationship to send it from an API Rest

I've got an entity I send in a datatable (webix) thanks to an API rest. In this entity I have several ManyToOne relationeships which I don't know how to display or edit.
In my datatable, when I try to display the field which is in a relathinship I've got this instead of the id
[object Object]
When I try to edit this field by adding the id I've got this error message
Type error: Argument 1 passed to ErpBundle\Entity\Trial::setGSponsor() must be an instance of ErpBundle\Entity\Sponsor, string given
I understand what I get in my datatable is an object but I don't know how to get the Id of the name instead (and being able to chose it from the datatable).
Do I have to edit the getter? The repository?
I've tried to edit my getter like that but of course it's not working:
/**
* Get gSponsor
*
* #return \ErpBundle\Entity\Sponsor
*/
public function getGSponsor()
{
return $this->gSponsor->getId();
}
Below a part of my code:
In the controller, I'm just rendering the template for the view. Webix just take the url as an argument to get the data (url: "rest->{{ path('erp_trialapi_cgetload') }}") The datatable is displayed automatically from what I send in my ApiController
Part of my entity
class Trial
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="ErpBundle\Entity\Sponsor")
*/
private $gSponsor;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set gSponsor
*
* #param \ErpBundle\Entity\Sponsor $gSponsor
*
* #return Trial
*/
public function setGSponsor(\ErpBundle\Entity\Sponsor $gSponsor = null)
{
$this->gSponsor->get = $gSponsor;
return $this;
}
/**
* Get gSponsor
*
* #return \ErpBundle\Entity\Sponsor
*/
public function getGSponsor()
{
return $this->gSponsor;
}
My API
class TrialApiController extends FOSRestController
{
/**
* #Rest\Get("/api_t/get")
*/
public function cgetLoadAction()
{
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository('ErpBundle:Trial');
$trials = $repo->findAll();
return $trials;
}
I'd appreciate any kind of help.
Thank you very much!

Doctrine 2 Modular App With Entity Replacement

I'm building an application using ZF2 and Doctrine2.
The ideia is to have a base app entity (lets call it UserEntity).
But in one Module A, I will have another UserEntity-like entity that will "upgrade" the base one, with new fields. And another Module B that will add more fields.
Ex:
BaseUserEntity {
protected $id;
// ...
}
ModuleAUserEntity extends BaseUserEntity {
protected moduleAId;
}
ModuleBUserEntity extends BaseUserEntity {
protected moduleBUserName;
}
Is it possible, somehow, to get a method so when I call UserEntity, it will return the full, upgraded-by-module, entity? Ex:
UserEntity {
protected $id;
// ...
protected moduleAId;
protected moduleBUserName;
}
Is there another way to achieve something like this? The possibility to "extension" of an entity?
I have 2 different approaches:
1.First one:
you should take a look at:
http://docs.doctrine-project.org/en/latest/reference/inheritance-mapping.html
thats the legitimate and recommend by doctrine way of doing this.
2. Second one
If you dont want doctrine to mess with the database, you can use a different aproach:
Create an interface that defines the common behavior of all classes
Write your base class, implementing that behavior.
write your child classes, implementing the interface, containing the new methods, and wrapping an instance of the base class
You implement the methods that are defined in the interface, but since they are already implemented in the parent class, you just bypass the call to the wrapped object.
So, you are using composition over inheritance to avoid doctrine (and probably you) to get crazy
In order to have a really clean behavior with doctrine, the database that i imagine is:
a table with the parent entity
a table with the child entity, containing
a foreign key with the id of the related parent entity (this is, the row in the parent table that contains the values asociated to this, since the child has to have the parent and the child fields)
all the extra columns
For instance:
Interface:
namespace DBAL\Entity;
interface IProfesional
{
public function setName($name);
public function getName();
public function getId();
}
Parent class:
namespace DBAL\Entity;
use Doctrine\ORM\Mapping as ORM;
use DBAL\Entity\User\IUserAware;
/**
* Profesional
*
* #ORM\Table(name="profesional")
* #ORM\Entity
*/
class Profesional implements IProfesional
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=45, nullable=true)
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* Set nombre
*
* #param string $nombre
* #return Profesional
*/
public function setName($nombre)
{
$this->name = $nombre;
return $this;
}
/**
* Get nombre
*
* #return string
*/
public function getNombre()
{
return $this->name;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
child class:
namespace DBAL\Entity;
use DBAL\Entity\User\IUserAware;
use Doctrine\ORM\Mapping as ORM;
/**
* Jugador
*
* #ORM\Table(name="jugador")
* #ORM\Entity(repositoryClass="DBAL\Repository\JugadorRepository")
*/
class Player implements IProfesional
{
/**
* #var integer
*
* #ORM\Column(name="profesional_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Profesional
*
* #ORM\OneToOne(targetEntity="Profesional")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="profesional_id", referencedColumnName="id", unique=true)
* })
*/
private $profesional;
/**
* Constructor: you create an empty Profesional,
so you are sure you will never use a reference to an in-existent object.
But anyways, if this is an entity loaded from the database by doctrine,
doctrine will fill that field whith an actual professional from the parent table,
based in the foreign key id
*/
public function __construct()
{
if(!isset($id)){
$this->profesional=new Profesional();
}
}
/**
* Set profesional
*
* #param \Profesional $profesional
* #return Jugador
*/
public function setProfesional( Profesional $profesional = null)
{
$this->profesional = $profesional;
return $this;
}
/**
* Get profesional
*
* #return \Profesional
*/
public function getProfesional()
{
return $this->profesional;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->profesional->getId();
}
///New fields, for instance:
/**
* #var integer
*
* #ORM\Column(name="height", type="integer", nullable=true)
*/
private $height;
public function getHeight()
{
return $this->height;
}
public function setHeight($h)
{
$this->height=$h;
}
}

Get empty collection of embedded documents

I want to get a collection of embedded documents. I put data into the database like this:
$dm->getRepository('BundleUserBundle:User')->addRatedPostById($user->getId(), new RatedPost($id, $type));
...which works fine. Now I see new data in mongo console, but when I'm trying to get this data via the getRatedPosts() method, it return null instead of the ArrayCollection. Whats wrong?
Context looks like this:
class User extends BaseUser {
/**
* #MongoDB\EmbedMany(targetDocument="RatedPost")
*/
protected $ratedPosts;
/**
* Add ratedPosts
*
* #param Bundle\UserBundle\Document\RatedPost $ratedPosts
*/
public function addRatedPost(\Bundle\UserBundle\Document\RatedPost $ratedPosts)
{
$this->ratedPosts[] = $ratedPosts;
}
/**
* Remove ratedPosts
*
* #param Bundle\UserBundle\Document\RatedPost $ratedPosts
*/
public function removeRatedPost(\Bundle\UserBundle\Document\RatedPost $ratedPosts)
{
$this->ratedPosts->removeElement($ratedPosts);
}
/**
* Get ratedPosts
*
* #return Doctrine\Common\Collections\Collection $ratedPosts
*/
public function getRatedPosts()
{
return $this->ratedPosts;
}
/**
* #MongoDB\EmbeddedDocument
*/
class RatedPost
{
/**
* #MongoDB\Int
*/
public $post;
/**
* #MongoDB\String
*/
public $rate;
...
}
I had exactly the same problems: saving was no problem but fetching the embedded document was. Turned out it was a "Symfony" caching problem (also on app_dev.php). Have you tried to remove your cache, this worked for me!

EntityNotFoundException in doctrine 2 proxy class

What are the possible causes of EntityNotFoundException while accessing the proxy class properties in doctrine 2? Anyway, the following is my entities' structure:
/**
* #ORM\Table(name="comments")
*
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="comment_type", type="smallint")
* #ORM\DiscriminatorMap({
* 1 = "VisitorComment",
* 2 = "MemberComment"
* })
*/
class Comment
{
//with common properties of its subclasses
}
subclasses are as follows:
/**
* #ORM\Table(name="member_comments")
*/
class MemberComment extends Comment
{
/**
* owning side
*
* #var Member $author
*
* #ORM\ManyToOne(targetEntity="Member")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id", nullable=false)
*/
private $author;
/**
* Set author
*
* #param Member $author
*/
public function setAuthor($author)
{
$this->author = $author;
}
/**
* Get author
*
* #return Member
*/
public function getAuthor()
{
return $this->author;
}
}
/**
* #ORM\Table(name="visitor_comments")
*/
class VisitorComment extends Comment
{
/**
* owning side
*
* #var Visitor $author
*
* #ORM\ManyToOne(targetEntity="Visitor")
* #ORM\JoinColumn(name="author_id", referencedColumnName="id", nullable=false)
*/
private $author;
/**
* Set author
*
* #param string $author
*/
public function setAuthor($author)
{
$this->author = $author;
}
/**
* Get author
*
* #return Visitor
*/
public function getAuthor()
{
return $this->author;
}
}
The exception occurs when I call $comment->getAuthor()->getFirstName() <assuming that author which is either a Member or Visitor entity has firstName property>. The getAuthor() in this case returns a proxy class of either VisitorProxy or MemberProxy.
Kindly help me. I'm still new to doctrine.
As Floricel found out, this can be caused by an invalid foreign key in a column that's points to the table that the Proxy class references.
#Dave Lancea is right I changed a FK to not Null and then started getting this error, manually updated a broken record, making it point to an existing entity and problem gone.

Joining-Table with Metadata Impairs Getters/Setters - Doctrine 2

I'm writing a feature which calls for the records of my joining table to carry extra metadata (Joining-Table with Metadata). I've attempted to implement this in accordance with this section of the Doctrine documentation.
See below for example Entity definitions.
The challenge now is that getGroups and setGroups do not yield/set Group entities (& the same is true from the Group instance perspective), but they yield GroupUser entities.
This adds a substantial delay to process of managing this relationships, which so far have been extremely smooth - for example, I cannot simply add, remove, or check for the existence of a Group to the collection which getGroups yields.
Can anyone identity any errors in my implementation, or else recommend a more fluid way of implementing this concept?
Thanks in advance for any input.
EDIT:
My main concern is this: using this implementation, retrieving a collection of Users from a Group entity requires this Entity method's mediation:
public function getUsers() {
return $this->users->map(function($groupUser){
return $groupUser->getUser();
});
}
I'm concerned that this could imply a major performance hit down the road. Am I incorrect?
Furthermore, how does one re-implement the setUsers method?
Group entity:
<?php
/**
* #Entity
* #Table(name="group")
*/
class Group {
/**
* #Column(type="integer", nullable=false)
* #Id
*/
protected $id = null;
/**
* #OneToMany(targetEntity="GroupUser", mappedBy="group")
* #var \Doctrine\Common\Collections\Collection
*/
protected $users;
}
User entity:
<?php
/**
* #Entity
* #Table(name="user")
*/
class User {
/**
* #Column(type="integer", nullable=false)
* #Id
*/
protected $id = null;
/**
* #OneToMany(targetEntity="GroupUser", mappedBy="user")
* #var \Doctrine\Common\Collections\Collection
*/
protected $groups;
}
Joining entity:
<?php
/**
* #Entity
* #Table(name="group_user")
*/
class GroupUser {
/**
* #Id
* #ManyToOne(targetEntity="User", inversedBy="groups")
* #JoinColumn(name="userId", referencedColumnName="id")
*/
protected $user;
/**
* #Id
* #ManyToOne(targetEntity="Group", inversedBy="users")
* #JoinColumn(name="groupId", referencedColumnName="id")
*/
protected $group;
/**
* #Column(type="integer")
*/
protected $relationship;
}
Related -
Same goal, slightly different approach, which consistently produced errors once the resulting collections were manipulated: http://www.doctrine-project.org/jira/browse/DDC-1323
Supports the approach, no technical details: Doctrine 2 join table + extra fields
I've found just two examples (see question) of entity definitions for this specific type of relationship, however no example code for how they're used. As such it was fairly unclear how fluid (or otherwise) the resulting setters & getters could be expected to be. Hopefully this code will help clear up the approach for anyone else making a similar attempt.
The ideal solution under the circumstances (thanks #doctrine # freenode) was to implement a custom repository - a more flexible & efficient place for creating & managing the association.
Example Custom Repository for Join-Table with Metadata Class - Solution accompanies code in original question
<?php
use Doctrine\ORM\EntityRepository;
class GroupUserRepository extends EntityRepository {
/**
* #param \User $user
* #param \Group $group
* #param integer $type One of the integer class constants defined by GroupUser
* #param string $role Optional string defining user's role in the group.
* #return \GroupUser
*/
public function addUserToGroup(User $user, Group $group, $relationship, $role = '') {
$groupUser = $this->findOneBy(array('user' => $user->getId(), 'group' => $group->getId()));
if(!$groupUser) {
$groupUser = new GroupUser();
$groupUser->setGroup($group);
$groupUser->setUser($user);
$groupUser->setRole($role);
$groupUser->setRelationship($relationship);
$this->_em->persist($groupUser);
}
return $groupUser;
}
/**
* #param \User $user
* #param \Group $group
* #return null
*/
public function removeUserFromGroup(User $user, Group $group) {
$groupUser = $this->findOneBy(array('user' => $user->getId(), 'group' => $group->getId()));
if($groupUser)
$this->_em->remove($groupUser);
}
}
Then, from the join-table class, modify the Entity meta-data accordingly to specify the custom repository.
<?php
/**
* #Entity(repositoryClass="\Path\To\GroupUserRepository")
*/
class GroupUser {
// ...
}
This causes the custom repository to yield in place of the default one, making a proxy method from the Entity class simple.
<?php
/**
* #Entity
*/
class Group {
/**
* #param \User $user
* #param integer $relationship One of the integer class constants defined by GroupUser
* #param string $role Optional string defining user's role in the group.
* #return \GroupUser
*/
public function addUser(User $user, $relationship, $role = '') {
return $this->_em->getRepository('GroupUser')
->addUserToGroup($user, $this, $relationship, $role);
}
}
And things are about as manageable as they were before.