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!
Related
I try to get all my objects DemandCab with their children object (DecisionCab).
My 2 entities
/**
* DemandCab.
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="DemandCabRepository")
*/
class DemandCab
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var DecisionCab
*
* #ORM\OneToMany(targetEntity="\My\CabBundle\Entity\DecisionCab", mappedBy="demandCab")
*/
private $decisionsCab;
/**
* #var \DateTime
*
* #ORM\Column(name="startDate", type="datetime")
*/
private $startDate;
/**
* #var \DateTime
*
* #ORM\Column(name="endDate", type="datetime", nullable=true)
*/
private $endDate;
/**
* #var int
*
* #ORM\Column(name="followup", type="integer", nullable=true)
*/
private $followup;
/**
* #var InfoCab
*
* #ORM\ManyToOne(targetEntity="\My\CabBundle\Entity\InfoCab", inversedBy="demandsCab")
*/
private $infoCab;
}
/**
* DecisionCab.
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="DecisionCabRepository")
*/
class DecisionCab
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var DemandCab
*
* #ORM\ManyToOne(targetEntity="\My\CabBundle\Entity\DemandCab", inversedBy="decisionsCab")
*/
private $demandCab;
/**
* #var bool
*
* #ORM\Column(name="decision", type="boolean", nullable=true)
*/
private $decision;
/**
* #var string
*
* #ORM\Column(name="motif", type="string", length=500, nullable=true)
*/
private $motif;
/**
* #var string
*
* #ORM\Column(name="role", type="string", length=255)
*/
private $role;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime", nullable=true)
*/
private $date;
/**
* #var DemandCab
*
* #ORM\ManyToOne(targetEntity="\My\CabBundle\Entity\DemandCab", inversedBy="decisionsCab")
*/
private $demandCab;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="\My\CabBundle\Entity\User", inversedBy="decisionsCab")
*/
private $user;
}
In my DemandCabRepository
public function findAllCompleted(){
$qb = $this->createQueryBuilder("dem");
$qb->select('dem, dec');
$qb->leftJoin("dem.decisionsCab", "dec");
$qb->andWhere("dem.completed = 1");
$qb->orderBy("dem.startDate", "DESC");
return $qb->getQuery()->getResult();
}
My DemandCab data
My DecisionCab data
When i dump result, only 2 decisions appear ...
... whereas when i use getArrayResult, i have my 4 decisions ...
The query is good but i dont understand why hydration remove DecisionCab object with attribute decision at 0 or 1 (only null are display).
I would like to understand why and is there a solution to get DemandCab object with all DecisionCab children object.
Thanks
I am able to reproduce your issue, but I am not sure if this is your case.
Anyway, my assumption is that you query the demand Entity joined with decision relation at least once with the help of a query builder. Maybe this is done in your action, in an event listener or somewhere else in your code.
So you may have something like:
$qb = $this->getDoctrine()
->getRepository(DemandCab::class)->createQueryBuilder("dem");
$qb->select('dem, dec');
$qb->leftJoin("dem.decisionsCab", "dec");
$qb->andWhere("dec.decision IS NULL");
$qb->orderBy("dem.startDate", "DESC");
$results = $qb->getQuery()->getResult(); // <-- the decisionsCab collection is hydrated but filtered
$qb2 = $this->getDoctrine()
->getRepository(DemandCab::class)->createQueryBuilder("dem");
$qb2->select('dem, dec');
$qb2->leftJoin("dem.decisionsCab", "dec");
$qb2->andWhere("dem.completed = 1");
$qb2->orderBy("dem.startDate", "DESC");
$q = $qb2->getQuery();
//$q->setHint(Query::HINT_REFRESH, true);
$results = $q->getResult();
The issue is in Doctrine\ORM\Internal\Hydration\ObjectHydrator, it has the property "initializedCollections" where already initialized collections are kept and the collections are hashed by the parent entity type and the entity itself. Unfortunately in the above case, the heydrator does not understand that the collection is filtered in the 1st query and uses it in the 2nd query in order to avoid rehydration.(github link)
The solution is to cause the query builder to refresh. Try the code:
$qb->orderBy("dem.startDate", "DESC");
$q = $qb->getQuery();
$q->setHint(Query::HINT_REFRESH, true); // <-- Tell the hydrator to refresh
return $q->getResult();
First you have initialize your class with the relation ManyToOne with an ArrayCollection.
And you don't need any of this 'DemandCabRepository'. All the work is done by Doctrine
I need help for create my entities whit doctrine 2 in my symfony3 app :
I would like my users can posts articles which content is:
title
author
either one unique image (upload file)
or one unique movie ($url)
What do you recommend ?
Should I build my article entity like this ?
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="author", type="integer")
*/
private $author;
/**
* #var ?
*
* #ORM\Column(name="image", type="?")
*/
private $image;
/**
* #var string
*
* #ORM\Column(name="url_movie", type="string")
*/
private $url_movie;
/**
* #var integer
*
* #ORM\Column(name="media", type="integer")
*/
private $media;
}
(in controller : if $media = 1 => this is an image, else this is a video)
Or use something like Relation One-To-One with a new entity "media" for example ?
What is the best way for my case ?
Yes, it's normal. Sadly, the discriminator column is meant to be used by Doctrine, database side, therefore it's not accessible in your entity. There's two possible way to achieve what you want:
The first, using the children class name:
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image"})
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
//...
/**
* Get article type.
*
* #return string
*/
public function getType()
{
// This will return "movie" or "image"
return strtolower(substr(strrchr(get_class($this), "\\"), 1));
}
}
/**
* Movie
*
* #ORM\Entity
*/
class Movie extends Article
{
/**
* #var string
*
* #ORM\Column(name="url", type="text")
*/
private $url;
//getter setter
}
/**
* Image
*
* #ORM\Entity
*/
class Image extends Article
{
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
//getter setter
}
The second, by declaring manually the type in your class:
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image"})
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*/
protected $type;
//...
/**
* Get article type.
*
* #return string
*/
public function getType()
{
return $this->type;
}
}
/**
* Movie
*
* #ORM\Entity
*/
class Movie extends Article
{
/**
* #var string
*
* #ORM\Column(name="url", type="text")
*/
private $url;
public function __construct()
{
$this->type = 'movie';
}
//getter setter
}
/**
* Image
*
* #ORM\Entity
*/
class Image extends Article
{
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
public function __construct()
{
$this->type = 'image';
}
//getter setter
}
Personnaly, I have a preference for the first solution. I find it cleaner, and more evolutive (this code will adapt if you have to add a third article type).
Of course, you can also use instanceof to determine which subclass is the Article entity you're manipulating.
I think using a Media entity and handling the media type in the Media entity is the best way.
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ...
/**
* #var Media
*
* #OneToOne(targetEntity="Media")
* #ORM\JoinColumn(name="media_id", referencedColumnName="id")
*/
private $media;
}
class Media
{
const TYPE_IMAGE = 'image';
const TYPE_MOVIE = 'movie';
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $url;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $type;
}
An other way to do it could be to use entity inheritance to have differents entities for images and movies - if you need to.
Ok I did a Single Table Inheritance on my article class :
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image"})
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
//...
}
/**
* Movie
*
* #ORM\Entity
*/
class Movie extends Article
{
/**
* #var string
*
* #ORM\Column(name="url", type="text")
*/
private $url;
//getter setter
}
/**
* Image
*
* #ORM\Entity
*/
class Image extends Article
{
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
//getter setter
}
That works great !
I have entity developer and comment and relationship Many comment to One developer. And I need form when I see all comment for developer and edit - add, delete in DB . What are the solutions to this problem
entity Comment:
class Comments
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Developer", inversedBy="comments")
* #ORM\JoinColumn(name="talent_id", nullable = true, referencedColumnName="id")
* */
protected $talent;
/**
* #var string
*
* #ORM\Column(name="added_by", type="string", length=10, nullable=true)
*/
private $added_by;
/**
* #var string
*
* #ORM\Column(name="comment", type="string", length=10, nullable=true)
*/
private $comment;
entity Developer:
class Developer extends CustomUser
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/////
/**
* #ORM\OneToMany(targetEntity="Comments", mappedBy="talent", cascade={"persist", "remove"})
*/
protected $comments;
Maybe need form in form but how to do this?
You are looking for field type collection.
Example usage of collection type
class Comments
{
....
/**
*
*#ORM\ManyToOne(targetEntity="Developer", inversedBy="developer_to_comments")
* #ORM\JoinColumn(name="developer_to_comments_id", referencedColumnName="id", nullable=false)
*
*/
private $comments_to_developer;
...
}
And class Developer
class Developer extends CustomUser
{
....
/**
*
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="Comments", mappedBy="comments_to_developer", cascade={"remove"})
*/
private $developer_to_comments;
public function __construct()
{
$this->developer_to_comments = new ArrayCollection();
}
....
}
And don't forget use Doctrine\Common\Collections\ArrayCollection
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.
I am using doctrine 2 within zend framework 2. To generate methods from existing entities using database table, the console command used is:
php doctrine-module orm:generate-entities --generate-annotations="true" --generate-methods="true" module
I have two namespaces Blog and Location
My question is:
1. When I run above code, only blog entities get updated. I want to know why it is behaving like this?
2. If I want to update only a specific entity, how can i do it?
The blog has two entities: Post and cateogry
Category.php
<?php
namespace Blog\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
/**
* Category
*
* #ORM\Table(name="Categories")
* #ORM\Entity
*/
class Category implements InputFilterAwareInterface
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", nullable=false)
*/
private $name;
protected $inputFilter;
/**
* Get Id
*
* #param integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Category
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
}
Post.php
namespace Blog\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Post
*
* #ORM\Table(name="posts")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Post
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", precision=0, scale=0, nullable=false, unique=false)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="content", type="text", precision=0, scale=0, nullable=false, unique=false)
*/
private $content;
/**
* #var \DateTime
*
* #ORM\Column(name="created_date", type="datetime", precision=0, scale=0, nullable=false, unique=false)
*/
private $createdDate;
/**
* #var \Blog\Entity\Category
*
* #ORM\ManyToOne(targetEntity="Blog\Entity\Category")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=true)
* })
*/
private $category;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
* #return Post
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set createdDate
*
* #param \DateTime $createdDate
* #return Post
*/
public function setCreatedDate($createdDate)
{
$this->createdDate = $createdDate;
return $this;
}
/**
* Get createdDate
*
* #return \DateTime
*/
public function getCreatedDate()
{
return $this->createdDate;
}
/**
* Set category
*
* #param \Blog\Entity\Category $category
* #return Post
*/
public function setCategory(\Blog\Entity\Category $category = null)
{
$this->category = $category;
return $this;
}
/**
* Get category
*
* #return \Blog\Entity\Category
*/
public function getCategory()
{
return $this->category;
}
}
The Location has Country.php
<?php
namespace Country\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Country
*
* #ORM\Table(name="countries")
* #ORM\Entity
*/
class Country
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=55, nullable=false)
*/
private $name;
}
OF Course everyone need a function on ZF2 that's turns to possible you generate a single entity. but those times you can't do it just by doctrine-orm-module. When you try to run doctrine in Symphony it's acceptable. I'm using doctrine2 with zf2 having the same issue.
I create a PHP Script that I call
Script to Create a Single Entity:
Choose Entity Name.
Generate All Entities on a Temp Folder.
Exclude All non-needed entities from folder.
Copy Single Entity to "src/$module/Entity" Folder.
Is a hack that i got to make it work property as you need.
Use the --filter option to do this.
php doctrine-module orm:generate-entities --filter="Post" ...