I have 2 classes:
Company:
class ComCompany
{
/**
* #var integer
*
* #ORM\Column(name="cmp_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $cmpId;
/**
* #var string
*
* #ORM\Column(name="cmp_name", type="string", length=100, nullable=true)
*/
private $cmpName;
/**
* #var integer
*
* #ORM\Column(name="cmp_code", type="integer", nullable=true)
*/
private $cmpCode;
/**
* #var \Catalog\WebBundle\Entity\ComCity
*
* #ORM\ManyToOne(targetEntity="Catalog\WebBundle\Entity\ComCity")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="cmp_city", referencedColumnName="cit_id")
* })
*/
private $cmpCity;
public function getCmpName()
{
return $this->cmpName;
}
public function getCmpCode()
{
return $this->cmpCode;
}
public function getCmpCity()
{
return $this->cmpCity;
}
}
And city
class ComCity
{
/**
* #var integer
*
* #ORM\Column(name="cit_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $citId;
/**
* #var string
*
* #ORM\Column(name="cit_name", type="string", length=255, nullable=true)
*/
private $citName;
public function getCitId()
{
return $this->citId;
}
public function getCitName()
{
return $this->citName;
}
}
This 2 tables have associations Company.comCity = City.citId
How to add getter method to ComCompany class to get City.citName ?
I have foreign keys and Entity is generated properly, but there not method for get citName from Company class
Just add the following code to your ComCompany class
public function getCityName()
{
return $this->cmpCity->getCitName();
}
You don't need no this getter method while you already have it in ComCity class. Because adding it (like answer suggested) is making duplicate code. You should use
$company->getCmpCity()->getCitName()
instead
And also: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
Both answers provided are absolutely correct. However, take into account that if you're using lazy loading an extra query will be triggered each time you call getCityName unless you use a JOIN in your DQL/Query Builder.
This can have terrible performance issues if you call getCityName in a loop so I thought it was worth mentioning it.
Related
I am upgrading from doctrine 2.3 to 2.5 and some of the relationships have broken.
I had a look other stack overflow questions. But there is only one id annotation is these classes and the join columns are named after their db column names, not their variable name on the entity
/**
* #Entity
* #Table(name="iso_country_shipping_display")
*/
class IsoCountryShippingDisplay {
/**
* #Id
* #GeneratedValue
* #Column(name="iso_country_shipping_display_id", type="integer", nullable=false)
*/
private $id;
/**
* #ManyToOne(targetEntity="namespace\Website")
* #JoinColumn(name="website_id", referencedColumnName="base_website_id", nullable=false, unique=false)
* #var Website
*/
private $baseWebsite;
/**
* #var IsoCountry
* #ManyToOne(targetEntity="namespace\IsoCountry")
* #JoinColumn(name="iso_3", referencedColumnName="iso_3", nullable=false, unique=false)
*/
private $isoCountry;
/**
* #return IsoCountry
*/
public function getIsoCountry()
{
return $this->isoCountry;
}
}
/**
* #Entity
* #Table(name="iso_country")
*/
class Website
{
/**
* #Id
* #Column(name="website_id", type="integer", nullable=false)
* #var integer
* #GeneratedValue
*/
protected $id;
/**
* #return int
*/
public function getId()
{
return $this->id;
}
}
I got the error message Missing value for primary key id on namespace\Website. I am not sure why I am getting this on doctrine 2.5 but not on 2.3.
Add * #GeneratedValue value annotation to the Website Entity.
I keep getting the error in the title when I want to login using the FOSUserBundle on Symfony. The problem is, I already have an "id" for my User table from my database so I don't want to create an "id" field like they ask on the FOSUserBundle guide. I don't understand why it would give me this error when there is no more "id" field in my code.
Is this "id" field mandatory?
Here is the code of my User class (here called "Utilisateurs")`use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
/**
* Utilisateurs
*
* #ORM\Table(name="utilisateurs", indexes={#ORM\Index(name="FK_UTILISATEURS_id_sexe", columns={"id_sexe"}), #ORM\Index(name="FK_UTILISATEURS_id_niveau", columns={"id_niveau"})})
* #ORM\Entity
*/
class Utilisateurs extends BaseUser
{
public function __construct()
{
parent::__construct();
}
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=25, nullable=true)
*/
private $nom;
/**
* #var string
*
* #ORM\Column(name="prenom", type="string", length=25, nullable=true)
*/
private $prenom;
/**
* #var \DateTime
*
* #ORM\Column(name="date_naissance", type="date", nullable=true)
*/
private $dateNaissance;
/**
* #var string
*
* #ORM\Column(name="url_photo", type="string", length=100, nullable=true)
*/
private $urlPhoto;
/**
* #var integer
*
* #ORM\Column(name="id_utilisateur", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idUtilisateur;
/**
* #var \Site\UserBundle\Entity\Sexes
*
* #ORM\ManyToOne(targetEntity="Site\UserBundle\Entity\Sexes")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_sexe", referencedColumnName="id_sexe")
* })
*/
private $idSexe;
/**
* #var \Site\UserBundle\Entity\Niveaux
*
* #ORM\ManyToOne(targetEntity="Site\UserBundle\Entity\Niveaux")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id_niveau", referencedColumnName="id_niveau")
* })
*/
private $idNiveau;`
As you can see I already have an "id_utilisateur" field which is the id of this entity.
And here is the code of the entity information in XML: The XML Code
Also here is a screenshot of the error I get when I try to log in: The Error
I think the problem is that per convention the id field is often called just id and in some places FOS UserBundle is expecting exactly that, e.g. in the UserProvider.
There are a few ways you can get around this. For instance you could just write your own UserProvder (using the one linked above as a reference) where you substitute the id with your field. You might have to do this in other places as well.
The easier solution would be to just change your entity to something like this:
/**
* #var integer
*
* #ORM\Column(name="id_utilisateur", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
public function getId() { return $this->id; }
Similarly in xml this would look like this:
<id name="id" column="id_utilisateur" type="integer">
<generator strategy="IDENTITY" />
</id>
This way in your entity you will use the expected property and accessor method, but in the background it will map to the database field id_utilisateur, so you you don't have to make any changes to your database.
This should already solve your problems. When a new user is generated Doctrine will take map $user->getId() to user_table.id_utilisateur automatically. If your existing code is making use of the old get-method you could just keep it around and mark it as deprecated:
/**
* #deprecated Use getId() instead.
*/
public function getIdUtilisateur()
{
return $this->getId();
}
I got a ParentClass like this
/** #ORM\MappedSuperclass */
class BaseValue
{
/**
* #var int
*
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var field
* #ORM\OneToMany(targetEntity="Field", mappedBy="value", cascade="all")
*/
protected $field;
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* #return field
*/
public function getField()
{
return $this->field;
}
/**
* #param field $field
*/
public function setField($field)
{
$this->field = $field;
}
}
And a child like this
* #ORM\Entity
* #ORM\Table(name="integers")
*/
class Integer extends BaseValue
{
/**
* #var integer
*
* #ORM\Column(name="value", type="integer", nullable=true)
*/
protected $value;
/**
* #return string
*/
public function getValue()
{
return $this->value;
}
/**
* #param string $value
*/
public function setValue($value)
{
$this->value = $value;
}
}
Now I would like to relate the child in another class like this
* #ORM\Entity
* #ORM\Table(name="fields")
*/
class Field
{
/**
* #var int
*
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var
* #ORM\ManyToOne(targetEntity="BaseValue", mappedBy="field", cascade="all")
* #ORM\JoinColumn(name="vid", referencedColumnName="id")
*/
protected $value; // but it does not work
It always gets me the following error:
[Doctrine\Common\Annotations\AnnotationException]
[Creation Error] The annotation #ORM\ManyToOne declared on property zmpim\Entity\Field::$value does not have a property named "mappedBy". Available properties: targetEntity, cascade, fetch, inversedBy
Both got mappedby,.. so the error seems to be senseless
Update:
A field has got values and labels. The values get inherited from BaseValue into IntegerValue, StringValue and later others...
My OneToMany Relation is a parent class of inheritance.
like this, now:
/** #ORM\MappedSuperclass */
class BaseValue
{
/**
* #var int
*
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var Field
* #ORM\OneToMany(targetEntity="Field", mappedBy="field", cascade="persist", orphanRemoval=true )
*/
protected $field;
And this is my ManyToOne:
/**
*
* #ORM\Entity
* #ORM\Table(name="fields")
*/
class Field
{
/**
* #var int
*
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var int|null
* #ORM\ManyToOne(targetEntity="BaseValue", inversedBy="value")
* #ORM\JoinColumn(name="vid", referencedColumnName="id", onDelete="CASCADE")
*/
protected $value;
It still give me an error, but now it is:
[Doctrine\ORM\ORMException]
Column name id referenced for relation from zmpim\Entity\Field towards zmpim\Entity\BaseValue does not exist.
Your Entity Field is the inversed side of your mapping so instead of using MappedBy declaration you have to use this
/**
* Inversed side
* #var int|null
* #ORM\ManyToOne(targetEntity="BaseValue", inversedBy="field")
* #ORM\JoinColumn(name="[your_name]", referencedColumnName="[id]", onDelete="CASCADE")
*/
protected $value;
To understand well inversedSide and MappedBy attributes you can read this:
Doctrine inverse and owning side
After reading again you are aware of your relationnal problem between the two of your entities, but if you declare ManyToOne annotation, you have to set inversedBy attribute or you'll get an error. And this is what you have.
You can't declare ManyToOne annotation with mappedBy attribute because it does not exist and throw an exception by Doctrine.
To resume :
ManyToOne association =>
* #ORM\ManyToOne(targetEntity="[yourEntity]", inversedBy="[Field]")
Be careful this side require the declaration of this :
* #ORM\JoinColumn(name="[your_name]", referencedColumnName="[id]", onDelete="CASCADE")
OneToMany =>
* #ORM\OneToMany(targetEntity="[An_Entity]",
* mappedBy="[Field]", cascade={"persist"}, orphanRemoval=true)
EDIT from your answer :
Your mapping is still incorrect, your data in InversedBy And mappedBy need to be switched.
I just installed the Translatable DoctrineExtensions from the StofDoctrineExtensionsBundle.
I have two entities "Space" and "Equipment" with many to one relation :
/**
* Space
* #ORM\Table(name="space")
* #ORM\Entity(repositoryClass="Project\ProjectBundle\Repository\PlaceRepository")
*/
class Space implements Translatable
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Project\ProjectBundle\Entity\Equipment", cascade={"persist"})
*/
private $equipments;
/**
* #var string
* #Gedmo\Translatable
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*/
private $name;
/**
* #Gedmo\Locale
*/
private $locale;
public function setTranslatableLocale($locale)
{
$this->locale = $locale;
}
}
And :
/**
* Equipment
* #ORM\Table("equipment")
* #ORM\Entity(repositoryClass="Project\ProjectBundle\Repository\EquipmentRepository")
*/
class Equipment implements Translatable
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #Gedmo\Translatable
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*/
private $name;
/**
* #Gedmo\Locale
*/
private $locale;
public function setTranslatableLocale($locale)
{
$this->locale = $locale;
}
}
In the spaceRepository, I have, in my get query function :
public function getSpace($id) {
$query->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
$query->setHint(\Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE,$locale);
$db = $this
->createQueryBuilder('s')
->andWhere('s.id = :id')
->setParameter('id', $id)
->leftJoin('s.equipments', 'e')
->addSelect('e');
$query = $db->getQuery();
$query->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker');
$query->setHint(\Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'en');
return $query->getSingleResult();
}
The TranslationWalker should get all the results in one single query but it doesn't work, and, finally, I have one query for each equipment (more than 150 queries).
How could I get all translated elements in one single query ?
Try to set the hydration mode manually as your issue is related to this problem.
Take a look at this answer on how to set the hydration mode. It worked for me.
In my case I simply added the following hints to a query
use Doctrine\ORM\Query;
use Gedmo\Translatable\Query\TreeWalker\TranslationWalker;
...
$query->setHydrationMode(TranslationWalker::HYDRATE_OBJECT_TRANSLATION);
$query->setHint(Query::HINT_REFRESH, true);
Hope that helps!
I have two entities Post and Comment.
Structure:
Post:
id
title
body
Comment:
id
post_id
body
active
class Post
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #ORM\Column(name="body", type="text")
*/
private $body;
/**
* #ORM\OneToMany(
* targetEntity="Comment",
* mappedBy="post"
* )
*/
private $comments;
class Comment
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="body", type="text")
*/
private $body;
/**
* #ORM\ManyToOne(
* targetEntity="Post",
* inversedBy="comments"
* )
* #ORM\JoinColumn(
* name="post_id",
* referencedColumnName="id"
* )
*/
private $post;
As a result when I want to get all comments for a post I use $post->getComments() and it works.
How I can add extra Where clauses into this relationship if I want to get only posts with active = 1.
I know that I can do it by DQL or queryBuilder but I want to know how I can do it by mapping
I think the cleanest way to retrieve only active comments is to use Doctrine's Criteria object in the getComments method of your Post entity
use Doctrine\Common\Collections\Criteria;
and
public function getComments()
{
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('active', 1));
return $this->comments->matching($criteria);
}
Edit
If you want to prevent multiple queries each time you retrieve the active comments, you'll need to store them in a local variable. Instead of modifying getComments, you could add $active_comments and getActiveComments, which will populate $active_comments and only query the db if $active_comments is false.
class Post {
private $active_comments;
public function getActiveComments()
{
if(!$this->active_comments) {
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('active', 1));
$this->active_comments = $this->comments->matching($criteria);
}
return $this->active_comments;
}