Accessing Related Entities in Symfony2 - php

I have a View entity that references an associated entity called ViewVersion. But if I name the variable anything other than viewVersion, e.g. just simple version, then I get an error:
Neither the property "viewVersion" nor one of the methods "getViewVersion()", "isViewVersion()", "hasViewVersion()", "__get()" exist and have public access in class "Gutensite\CmsBundle\Entity\View\View".
All the getters and setters are created through php app/console doctrine:generate:entities but they are for getVersion() and not getViewVersion().
Question: So, is there some unspoken rule that associated entities MUST be named the same as their class name?
Entity Definition
/**
* #ORM\Entity
* #ORM\Table(name="view")
* #ORM\Entity(repositoryClass="Gutensite\CmsBundle\Entity\View\ViewRepository")
*/
class View extends Entity\Base {
/**
* #ORM\OneToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\ViewVersion", inversedBy="view", cascade={"persist", "remove"}, orphanRemoval=true)
* #ORM\JoinColumn(name="versionId", referencedColumnName="id")
*/
protected $version;
/**
* #ORM\Column(type="integer", nullable=true)
*/
protected $versionId = NULL;
}

FYI, the variables for associated entities can be whatever you want.
This was caused by a predefined formType still referencing "viewVersion". The first variable in a form $builder->add() is a reference to the specific variable in the entity. I had viewVersion listed there still, and when I audited my code, I assumed it was just a generic reference (without any requirement) or possibly a reference to the Entity class, so I didn't change it:
$builder->add('viewVersion', new ViewVersionType(), array(
'label' => false
));
The SOLUTION to this problem was to change viewVersion to version so that it references an actual variable on the entity. Obviously...
$builder->add('version', new ViewVersionType(), array(
'label' => false
));

Related

Symfony Entity Type reference contains String not integer when saving

I'm getting an exception when trying to save an Office object to the database using Symfony/Doctrine as follows:
An exception occurred while executing 'UPDATE offices SET county_postcode = ? WHERE id = ?' with params [{}, 1]:
SQLSTATE[HY000]: General error: 1366 Incorrect integer value: 'Co. Longford' for column 'county_postcode' at row 1
My Office class contains a mapping to the County class as follows:
/**
* #ORM\Entity
* #ORM\Table(name="offices")
*/
class Office
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #param mixed $id
/**
* #ORM\Column(type="integer", length=11 )
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\County")
*/
private $countyPostcode;
My OfficeType uses the EntityType
class OfficeType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('officeName')->add('addressLine1')->add('addressLine2')- >add('addressLine3')
->add('eirCode')->add('landlineTelephone')->add('mobileTelephone')->add('isActive')
->add('countyPostcode', EntityType::class, array(
// query choices from this entity
'class' => 'AppBundle:County',
'choice_label' => function ($county) {
return $county->getCountyName();
}));
}
This works fine and renders the countyNames as strings in the select control on the form. Checking the form data in my browser shows that the form contains the correct integer values as does inspecting the object using var_dump($office) but as per the error at the start of the question it throws
SQLSTATE[HY000]: General error: 1366 Incorrect integer value: 'Co. Kilkenny' for column 'county_postcode' at row 1
['Co. Kilkenny' is the string value, not the related integer type]
I've tried the solution noted here and added __toString() method to my County class but this didn't resolve the issue
public function __toString() {
return $this->countyName; }
Anyone any suggestions?
Thanks
John
Edit-
eventually figured it out, thanks in part to Malcolm's comment
Apparently you can’t have both an
#ORM\Column
and
#ORM\JoinColumn
annotations together on the same field so the annotation needs to become
/**
* #ORM\ManyToOne(targetEntity="County")
* #ORM\JoinColumn(name="county_postcode", referencedColumnName="id")
*/\
The association mapping between "Office" and "County" is wrongly defined, see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-one-unidirectional.
Remove this line * #ORM\Column(type="integer", length=11 ) and update your schema.
As noted in the edits above, it's wasn't sufficient to remove the #ORM\Column annotation, this simply broke the views.
The issue is that you can't have both
#ORM\Column
and
#ORM\JoinColumn
annotations together on the same field
So the correct annotation needs to become
/**
* #ORM\ManyToOne(targetEntity="County")
* #ORM\JoinColumn(name="county_postcode", referencedColumnName="id")
*/\
One other step that's also essential is to ensure that the referenced entity, in this case County, defines a _toString() method which returns the String name of referenced entity
public function __toString() {
return $this->countyName; }
Thanks to both Malcolm and Renan for their help in resolving this

Unidirectional many-to-one relation in Doctrine MongoDB ODM without MongoId

I'm trying to port the following Doctrine ORM example to Doctrine ODM.
<?php
/** #Entity */
class User
{
/**
* #ManyToOne(targetEntity="Address")
* #JoinColumn(name="address_id", referencedColumnName="address_id")
*/
private $address;
}
/** #Entity */
class Address
{
// ...
}
I'm looking for the counterpart of #JoinColumn(), which I couldn't find in the documentation. Basically, I want to set the referencing field name and the referenced field name myself. How can I do this?
In MongoDB you can only reference by id, but you're not limited to using MongoID's. In fact, you can use whatever you like, including objects as id's.
This is what you should do in MongoODM, to have a property of Address act as id and User will reference Address by the value of that field. You should also set simple=true for the reference.
/**
* #Document
*/
class User
{
/**
* #ReferenceOne(targetDocument="Address", simple=true)
*/
protected $address;
}
/**
* #Document
*/
class Address
{
/**
* #Id(strategy="NONE")
*/
protected $someProperty;
}
Remember that if you change the value of that property in any of the addresses that are referenced by one or more users, that reference will become corrupt and cause some painful errors in doctrine ODM.

How to handle Primary/Secondary/Normal statuses for entity in Symfony2

I am developing an application and I came across the following: Lets say I have an entity called Contact, that Contact belongs to a Company and the Company has a Primary Contact and a Secondary Contact and also has the remaining Contacts which I've named Normal.
My question is, what is the best approach for this when talking about entities properties and also form handling. I've though about two things:
Having 2 fields on the Company entity called PrimaryContact and SecondaryContact and also have a one-to-many relationship to a property called contacts.
What I don't like (or I'm not 100% how to do) about this option is that on the Contact entity I would need an inversedBy field for each of the 2 one-to-one properties and also 1 for the one-to-many relationship and my personal thought is that this is kind of messy for the purpose.
Having a property on the Contact entity called Type which would hold if it's primary, secondary or normal and in the Company methods that has to do with Contacts I would modify it and add the getPrimaryContact, getSecondaryContact, etc.
What I don't like about this option is that I would need to have 2 unmapped properties for the Company and I would need to do a lot on the form types in order to get this to work smoothly.
My question is what is the best approach for this structure and how to deal with forms and these dependencies. Let me know if this is not clear enough and I will take time and preparate an example with code and images.
I'm not yet a Symfony expert but i'm currently learning entites manipulation and relations !
And there is not simple way to do relations with attributes.
You have to create an entity that represent your relation.
Let's suppose you have an entity Company and and entity Contact
Then you will have an entity named CompanyContact whick will represent the relation between your objects. (you can have as many attributes as you wish in your relation entity). (Not sure for the Many-to-One for your case but the idea is the same)
<?php
namespace My\Namespace\Entity
use Doctrine\ORM\Mapping as ORM
/**
* #ORM\Entity(repositoryClass="My\Namespace\Entity\CompanyContactRepository")
*/
class CompanyContact
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="contact_type", type="string", length=255)
*/
private $contactType;
/**
* #ORM\ManyToOne(targetEntity="My\Namespace\Entity\Company")
* #ORM\JoinColumn(nullable=false)
*/
private $company;
/**
* #ORM\ManyToOne(targetEntity="My\Namespace\Entity\Contact")
* #ORM\JoinColumn(nullable=false)
*/
private $contact;
}
And in your controller you can do this:
$em = $this->getDoctrine()->getManager();
$company = $em->getRepository('YourBundle:Company')->find($yourCompanyId);
$yourType = "primary";
$companyContacts = $em->getRepository('YourBundle:CompanyContact')
->findBy(array('company' => $company, 'type' => $yourType));
What do you think about this approach ?
If i learn more soon i will get you posted ;)
Thanks to #Cerad this is the following approach I took:
I have a OneToMany property on the Company to hold all the contacts.
Implemented the getPrimaryContact/setPrimaryContact methods and looped through all the contacts and retrieving the one of the type I want. Did the same for the secondary.
On the Form type of the company my issue was that I had the 'mapped' => 'false' option, I removed this since I implemented the getters and setters SF2 knows it has to go to these methods.
`
<?php
namespace XYZ\Entity;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class Company
{
...
/**
* #ORM\OneToMany(targetEntity="\XYZ\Entity\Contact", mappedBy="company", cascade={"persist", "remove"})
*/
private $contacts;
public function getPrimaryContact() { ... }
public function setPrimaryContact(Contact $contact) { //Set the type of $contact and add it $this->addContact($contact) }
public function getSecondaryContact() { ... }
public function setSecondaryContact(Contact $contact) { //Set the type of $contact and add it $this->addContact($contact) }
}`
And for the Form Type I have:
`
class CompanyType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
...
->add('primaryContact', new ContactType())
->add('secondaryContact', new ContactType())
}
...
}`
With this set everything runs smoothly and I can CRUD without much struggle.

Doctrine2 ManyToMany Self referencing

I got problems with persisting many to many self referencing relations.
I receive error:
The class 'Doctrine\ORM\Persisters\ManyToManyPersister' was not found in the chain configured namespaces
This happens when I remove all children form item saved with them.
Leaving at least one don't make error happen. Also if I initially save entity with no children everything works fine.
/**
* West\AlbumBundle\Entity\Album
*
* #ORM\Table(name="albums")
* #ORM\Entity(repositoryClass="West\AlbumBundle\Entity\AlbumRepository")
* #ORM\HasLifecycleCallbacks
*/
class Album extends Entity implements CrudEntity
{
/**
* #ORM\ManyToMany(targetEntity="Album")
* #ORM\JoinTable(name="albums_relations",
* joinColumns={#ORM\JoinColumn(name="album_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="related_album_id", referencedColumnName="id")}
* )
* #var ArrayCollection
*/
protected $related_albums;
}
If you're testing with Symfony2 forms remember to set
"by_reference" => false
I've found that the problem happens when the method UnitOfWork.scheduleCollectionDeletion is called, for example, from MergeDoctrineCollectionListener.onBind() and the PersistentCollection object has been cloned ( 'by_reference' = false )
A quick fix to this problem is to comment the following line in the MergeDoctrineCollectionListener class:
//$collection->clear();

Accessing relations of a table with inheritance

I have an Inheritance class as shown here:
As you can easily see users, buildings and hotels have addresses (more than one) and address table keeps the id of the owner in whose column.
Is my logic correct?
Let's say I want to get the address of user (or buildings or hotels) whose id is 2; must I run a DQL statement (and how?) or can I get it with find() function without DQL?
And I'll be happy if you give example since Doctrine documentation doesn't help much.
Thanks.
Edit: users, buildings and hotels are just symbolic names that is why they can have multiple addresses otherwise buildings and hotels would have only one address.
Edit 2:I think I couldn't make myself clear, when I talk about the Class Table Inheritance I mean entity class has the Discriminator column as
/**
* ...
*
* #DiscriminatorColumn(name="classname", type="string")
* #DiscriminatorMap({"Entities\users" = "Entities\users",
* "Entities\buildings" = "Entities\buildings"}) ... etc
*/
Each and every subclass is related to parent (Entity) with the foreign key relation as "id". But of course doctrine creates this relation already for me.
Usually an Address is a typical value object. Value objects are usually stored with the entity compositing the value object so it is neither about relations nor about class table inheritance. If your domain indicates otherwise (e.g. you can do something with your address, meaning), they might be an entity, than entity Hotel holds an entity Address (persisted in a n:m relation table) and entity Building holds and Address too (in a different n:m relation table).
If you go the value object route, things are different. You would store the address with the Building entity as well as with the Hotel entity (as you would do it with other value objects may it be Moneyor Email or Password). So you don’t need relations at all, just a few more fields. The issue with Doctrine 2 is, that it does not support Component mapping. Component mapping would be used to nicely store value objects. T accomplish the same thing with Doctrine 2, you would implement a #prePersist and a #postLoad handler like that:
class Hotel
{
private ;
/** These fields are persisted */
/** #Column(type=string) */
private $addressStreet;
/** #Column(type=string) */
private $addressCity;
/** #Column(type=string) */
private $addressZip;
/** #Column(type=string) */
private $addressCountry;
/** #prePersist */
public function serializeValueObjects()
{
$this->addressStreet = ->address->getStreet();
$this->addressCity = ->address->getCity();
$this->addressZip = ->address->getZip();
$this->addressCountry = ->address->getCountry();
}
public function unserializeValueObjects()
{
$this->address = new Address(->addressStreet, ->addressCity, ->addressZip, ->addressCountry);
}
}
As you need to serialize/unserialize Address value objects in various places, you might want to extract the serializing code into a separated class.
/**
*
* #Entity
* #Table(name="proposaltemplate")
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="entitytype", type="string")
* #DiscriminatorMap({"proposal" = "ProposalTemplate","page" = "PageTemplate"})
*
*/
abstract class AbstractProposalTemplate
{
/**
*
* #var integer
* #Id
* #Column(type="integer")
* #generatedValue(strategy="AUTO")
*
*/
private $id;
}
next
#Entity
class ProposalTemplate extends AbstractProposalTemplate
{
#Id
#Column(type="integer")
#generatedValue(strategy="AUTO")
private $id;
}
next another class
#Entity
class PageTemplate extends AbstractProposalTemplate
{
/**
*
* #var integer
* #Id
* #Column(type="integer")
* #generatedValue(strategy="AUTO")
*
*/
private $id;
}
So you've got a superclass called "Entity", which has subclasses "User", "Building", and "Hotel".
Your "Entity" entity should have a OneToMany relation to Address. Let's imagine it looks like this, in your Entity definition:
/**
* #OneToMany(targetEntity="Address", mappedBy="whose"
*/
protected $addresses;
This is a more-or-less fine approach, though the use of inheritance is a little smelly.
Then if you want to iterate over the addresses, from inside User, Building, or Hotel:
foreach($this->addresses as $address){
//do something with adderess
}
Does that answer your question?

Categories