Symfony2 form and Doctrine OneToOne relationship - forms

I have 2 entities: User and Avatar. Each user can choice one avatar from a list, so I think this is a One2One unidirectional relationship. The problem is that the field avatar_id is always NULL in the db when I save the form.
Let's see the code:
class User implements UserInterface
{
/**
* #var int $avatarId
*
* #ORM\Column(name="avatar_id", type="integer", nullable=true)
*/
private $avatarId;
/**
* #var Avatar
*
* #ORM\OneToOne(targetEntity="Avatar", cascade={"persist", "remove"})
*/
private $avatar;
}
When I var_dump the user object before saving, the field avatarId contains the Avatar object but the id is not saved. What I'm doing wrong?
["avatarId":"Test\UserBundle\Entity\User":private]=>
object(Test\UserBundle\Entity\Avatar)#419 (5) {
["id":"Test\User\Bundle\Entity\Avatar":private]=>
int(3)
["imageName":"Test\UserBundle\Entity\Avatar":private]=>
string(14) "death-dark.jpg"
}

You don't need the avatarId field, since the avatar field will take care of it automatically. You can use the #JoinColumn annotation to set the referencing column name explicitly.

A few things:
ID columns are typically generated with the following annotations:
/**
* #ORM\Id #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
This tells the ORM to automatically generate a value when you create a new object, thus when you dump the object it will have a value before persisting.
And a note on your OneToOne relationship, if Avatars can be used by multiple people from that list, that would be a ManyToOne relationship (Many users to one avatar, one avatar to many users).

Related

Add refrences to MongoDB schema within Symfony project

I'm trying to create MongoDB database with some references within Symfony.
In my context I have 2 documents Customer and Meeting, One Customer can have Many Meeting so that what I did :
Meeting.php
<?php
namespace FrontOfficeBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class Meeting
{
/**
* #MongoDB\Id
* #MongoDB\ReferenceOne(targetDocument="Customer")
*/
protected $id;
/**
* #MongoDB\Field(type="timestamp")
*/
protected $creationDate;
...
Customer.php
<?php
namespace FrontOfficeBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class Customer
{
/**
* #MongoDB\Id()
* #MongoDB\ReferenceMany(targetDocument="Meeting")
*/
protected $id;
/**
* #MongoDB\Field(type="string")
*/
protected $username;
...
and then when I run the command line:
php bin/console doctrine:mongodb:schema:update
I got :
No identifier/primary key specified for Document 'FrontOfficeBundle\Document\Meeting'. Every Document must have an identifier/primary key.
I tried by using #MongoDB\UniqueIndex() but no way.
I think that #MongoDB\Id is supposed as an identifier !!!
Versions
Symfony 3.2
MongoDB 3.4.4
Any ideas ?
Thanks you.
Finally I found a solution, first I added a field called $meetings in the document Customer and an other $customer in the document meeting like this :
Customer.php
/**
* #MongoDB\ReferenceMany(targetDocument="meeting", mappedBy="customer")
*/
protected $meetings;
Meeting.php
/**
* #MongoDB\ReferenceOne(targetDocument="customer", inversedBy="meetings")
*/
protected $customer;
Then run the command line to generate setters and getters:
php bin/console doctrine:mongodb:generate:documents mybundleBundle
And when I run a fixtures (Creating one customer then its meeting), everything work fine, you'll notice that the relationship 1 to many has been defined in your document (I'm using compass to display mongoDB) as a One-to-Squillions model (for more details about 1-to-N relationship : (https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design-part-1) that's mean the son document (meeting) contains the reference of the parent as shown below :
Customer
Meeting
In MongoDB the #MongDB/Id is a primary key and the foreign key and never try to define references to this unique field.
Thanks for your attention.

Persisting a document suddenly stops returning next identifier value (ALNUM strategry)

I have Symfony 2.6 application and using doctrine-odm-bundle 3.0.2 (doctrine-odm: 1.1.2, mongodb: 1.4.0).
My document has referenceMany and referenceOne in attributes and when I create new instance of it, fill fields and persist - it goes fine in the begining. I can create few nearly empty documents, with referenced document(s) or without and it works fine. At some point I am trying to add new item in the database and getting an error:
E11000 duplicate key error collection: test.Product index: _id_ dup key: { : 0 }
The message is clear - I can see that there was a document added to the collection with id = 0, therefore second one can't go -> duplicate entry. But why it suddenly starts to return "0" for id? Even though, I checked doctrine_increment_ids collection - counter for id is being incremented. But $product->getId() becomes "0" after persist.
If I drop the database and start all over - it works, I can still add new products in the collection. Let's say I successfully created 12 products. Creating 13th resulting a document with id=0 being persisted in the collection. 14th fails with duplicate error.
Can you please help to troubleshoot or suggest an idea where does it go wrong?
P.S> I am not considering an upgrade of Symfony2 (at this point) neither as doctrine-odm-bundle (it depends on newer Symfony2 as well) - migration efforts are quite high and I am not sure it will fix the issue. First I want to find out the root cause.
// Document Product
/**
* #MongoDB\Document
* #MongoDB\HasLifecycleCallbacks
*/
class Product
{
/** #MongoDB\Id(strategy="ALNUM", type="int") */
protected $id;
/**
* #Gedmo\ReferenceOne(type="entity", class="Entity\User", inversedBy="products", identifier="userId")
*/
protected $user;
/**
* #MongoDB\Field(name="user_id", type="int")
*/
protected $userId;
/**
* #MongoDB\ReferenceMany(
* targetDocument="Picture",
* discriminatorMap={"file" = "File", "picture" = "Picture"},
* discriminatorField="discr",
* defaultDiscriminatorValue="picture"
* )
* #Assert\Valid
*/
protected $pictures;
...
}
// Entity User
/**
* User entity
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var ArrayCollection $textures
*
* #Gedmo\ReferenceMany(type="document", class="Document\Product", mappedBy="user")
*/
protected $products;
...
}
// Document Picture
/**
* #MongoDB\Document
* #MongoDB\InheritanceType("SINGLE_COLLECTION")
* #MongoDB\DiscriminatorField("discr")
* #MongoDB\DiscriminatorMap({"file" = "File", "picture" = "Picture"})
* #MongoDB\DefaultDiscriminatorValue("picture")
* #MongoDB\HasLifecycleCallbacks
*/
class Picture
{
/**
*
* #MongoDB\Id(strategy="ALNUM", type="int")
*/
protected $id;
/**
* #MongoDB\ReferenceOne(targetDocument="Product")
*
* #var Product $product
*/
protected $product;
...
}
Documentation reading always helps (generation strategies). Basically, strategy="ALNUM" and type="int" just can't go together :)
Change strategy to INCREMENT and remove type="int" if you want to have integers in your _id.
Or you can change type to string to continue with _id being an alphanumeric string.

How to get the type of MongoDB Document in twig?

I used Symfony2 and Doctrine MongoDBBundle and I have simple Single Collection Inheritance classes. How can I know what type of document it is in a twig template? For example the base class is Entity and extended by User and Organization, in listing those in a twig template I'd like to know what type of entity it is (i.e. whether it's a User or an Organization). I wonder if it's possible to get the value of the DiscriminatorField of the document.
/**
* #MongoDB\Document(collection="entity")
* #MongoDB\InheritanceType("SINGLE_COLLECTION")
* #MongoDB\DiscriminatorField(fieldName="type")
* #MongoDB\DiscriminatorMap({"user"="User", "shop"="Shop"})
*/
class Entity
{
/**
* #MongoDB\Id
*/
protected $id;
protected $entityType;
public function getEntityType()
{
return $this->entityType;
}
}

Symfony/Doctrine: "Catchable Fatal Error: Object of class <type> could not be converted to string" when persisting

A symfony2 application has a Job entity that has as a property of type WebSite.
A simplified representation of this without other properties or methods:
/**
* #ORM\Entity
* #ORM\Table(name="Job")
*/
class Job
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="integer", name="website_id", nullable=false)
* #ORM\ManyToOne(targetEntity="Example\ExampleBundle\Entity\WebSite")
*/
protected $website;
}
Symfony/Doctrine is trying to cast the website property to a string when persisting resulting in the error:
Catchable Fatal Error: Object of class
Example\ExampleBundle\Entity\WebSite could not be converted to string
in /vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php line 131
I believe the above #ORM\Column annotation denotes the website property to be an integer. I don't understand why Symfony/Doctrine wishes to try and convert the website property to a string.
Non-ideal workarounds I have tried in an attempt to resolve the matter:
Adding __toString() method to WebSite, returning a string representation of the id property, causes the correct value to ultimately end up in the Job.website_id datafield; this workaround is impractical as I will in the future need __toString() to be available to present a string elsewhere in the application.
Removing the #ORM\Column annotation from the website property; this results in all future diff-generated migrations (php app/console doctrine:migrations:diff) removing the 'not null' aspect of the relevant database field.
Are there any changes I should make to the above annotation to inform Symfony/Doctrine to call the getId() method of WebSite to get what it needs when persisting? Are there any changes I could make to WebSite to achieve the same?
What changes are required to ensure that the above annotation can remain in place such that Doctrine ultimately calls WebSite.getId() to get what it needs when persisting instead of trying to cast the object to a string?
You have to remove the #ORM\Column(type="integer" annotation from the $website property.
To be sure that the website_id column keeps the NOT NULL constraint, add a JoinColumn annotation with nullable=false in it:
/**
* #var Example\ExampleBundle\Entity\WebSite
*
* #ORM\ManyToOne(targetEntity="Example\ExampleBundle\Entity\WebSite")
* #ORM\JoinColumn(name="website_id", referencedColumnName="id", nullable=false)
*/
protected $website;

Querying data from a child model

I have 3 models that I have setup thus far in a simple application I am working on:
So far I have these models:
UserAccountEntity - Top level Table (Has a One-Many Relationship to UserAccountEntityStrings)
UserAccountEntityStrings - Child Table (Has a Many-One relation ship to UserAccountEntity and EavAttributes
EavAttributes - Lookup Table
When I query data from my top level table, I get the schema,association information for the child table. But I do not get any of the persisted data from the child table.
What I expected the results to be were, the data from the top level model and the data from the associated child model. Any help with this is greatly appreciated.
A note that may be helpful, I am using Zend 1.11.10 and Doctrine 2
This is what my query looks like:
$users = $em->createQuery('select u from Fiobox\Entity\UserModule\UserAccountEntity u')->execute();
Zend_Debug::dump($users[0]);
This is the association in my top level model:
/**
*
* #param \Doctrine\Common\Collections\Collection $property
* #OneToMany(targetEntity="UserAccountEntityStrings",mappedBy="UserAccountEntity", cascade={"persist","remove"})
*/
private $strings;
These are the associations in my child model:
/**
*
* #var UserAccountEntity
* #ManyToOne(targetEntity="UserAccountEntity")
* #JoinColumns({
* #JoinColumn(name="entity_id", referencedColumnName="entity_id")
* })
*/
private $user;
/**
* #var EavAttribute
* #ManyToOne(targetEntity="Fiobox\Entity\EavModule\EavAttributes")
* #JoinColumn(name="attribute_id", referencedColumnName="attribute_id")
*/
private $attributes;
Have you actually tried anything?
Doctrine will lazy load stuff for you. Your var_dump probably shows persistent collections of proxy objects for your child objects. But if you access them, they'll be loaded automatically:
<?php
$users = $em->createQuery('select u from Fiobox\Entity\UserModule\UserAccountEntity u')->fetchAll();
foreach($users as $u){
foreach($u->strings as $s){
var_dump($s);
}
}
If you know that you're going to need all that child data, you might as well force a fetch-join in your DQL:
<?php
$users = $em->createQuery('select u, s from Fiobox\Entity\UserModule\UserAccountEntity u JOIN u.strings s')->fetchAll();