My entity class is:
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\TipiVendita", inversedBy="idAgente")
* #ORM\JoinTable(name="tipi_vendita_agenti",
* joinColumns={
* #ORM\JoinColumn(name="id_agente", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="id_tipo_vendita", referencedColumnName="id")
* }
* )
*/
private $idTipoVendita;
/**
* Constructor
*/
public function __construct()
{
$this->idTipoVendita = new \Doctrine\Common\Collections\ArrayCollection();
}
and when i use the command 'doctrine:generate:entities', methods that generates are wrongly named:
/**
* Add idTipoVendita
*
* #param \AppBundle\Entity\TipiVendita $idTipoVendita
* #return Agenti
*/
public function addIdTipoVenditum(\AppBundle\Entity\TipiVendita $idTipoVendita)
{
$this->idTipoVendita[] = $idTipoVendita;
return $this;
}
/**
* Remove idTipoVendita
*
* #param \AppBundle\Entity\TipiVendita $idTipoVendita
*/
public function removeIdTipoVenditum(\AppBundle\Entity\TipiVendita $idTipoVendita)
{
$this->idTipoVendita->removeElement($idTipoVendita);
}
/**
* Get idTipoVendita
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getIdTipoVendita()
{
return $this->idTipoVendita;
}
How can i have addIdTipoVentita instead of addIdTipoVenditum, removeIdTipoVendita instead of removeIdTipoVenditum?
Update
The issue is that doctrine don't generate properly my entity! It generates methods names with the doctrine inflector component and add these kind of suffixes. How can i configure it in the right way?
Riccardo, You should name your properties in English.
And you should be aware that you are in a Object world, you work with objects and not with object_ids.
So the Entity TipiVendita should be named something like SellType (english, singular).
While the `idTipoVendita' field, if I understand, should enforce an N-M relation with SellType and Agents, so it should be named something like sellTypes (english, plural because it is a collection).
To make a long story short idTipoVendita - beeing a field for a collection, is seen as a plural form and is singularized in idTipoVenditum (just like "Curricula" and "Curriculum").
Best regards.
Related
I am beginning to suspect that this doesn't work because in my use case it just doesn't -- as opposed to me missing something -- but I have to consult your expertise to make sure, and to see if anyone can suggesta workaround.
I have a Many-to-Many situation that I am implementing with an association class, so we have one-to-many/many-to-one associations between the 3 participating classes. There is an Interpreter entity representing a person, and a Language entity representing a spoken language (actually a working language pair, but one half of the pair is understood to be English in this anglocentric application). An Interpreter can have multiple languages, and a Language is among the working languages of multiple interpreters. We need to manage other attributes of the interpreter-language, hence the InterpreterLanguage class.
When I call $interpreter->removeInterpreterLanguage($interpreterLanguage); followed by $entityManager->flush(), the in-memory Interpreter entity has one fewer elements in its $interpreterLanguages collection as you would expect, and there is no error or Exception thrown, but in the database here's what happens: nothing.
I have tried this in an MVC context, with ZendFramework 3 and a bound Zend\Form\Form with fieldsets, and when that drove me nuts I wrote a CLI script to try to examine the problem -- same result. Maybe it's worth noting that for updating scalar properties it's working fine.
I apologize for not including a link to the discussion of this issue that I read earlier -- can't find it now, for some reason. But I recall someone saying that it just doesn't work because Doctrine sees the M:1 on the other side, and therefore won't delete, and you have to say $entityManager->remove($object) to get it done. My experimental CLI script appears to confirm this. Nevertheless, I'd like to rule out the possibility that I am doing something wrong.
Any ideas? Suggestions for solving?
So here's my Language entity:
/** module/InterpretersOffice/src/Entity/Language.php */
namespace InterpretersOffice\Entity;
use Doctrine\ORM\Mapping as ORM;
use Zend\Form\Annotation;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Entity class representing a language used by an Interpreter.
*
* #Annotation\Name("language")
* #ORM\Entity(repositoryClass="InterpretersOffice\Entity\Repository\LanguageRepository")
* #ORM\Table(name="languages",uniqueConstraints={#ORM\UniqueConstraint(name="unique_language",columns={"name"})})
*/
class Language
{
/**
* entity id.
*
* #ORM\Id
* #ORM\GeneratedValue #ORM\Column(type="smallint",options={"unsigned":true})
*/
protected $id;
/**
* name of the language.
*
* #ORM\Column(type="string",length=50,nullable=false)
*
* #var string
*/
protected $name;
/**
* comments.
*
* #ORM\Column(type="string",length=300,nullable=false,options={"default":""})
*
* #var string
*/
protected $comments = '';
/**
*
* #ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="language")
*/
protected $interpreterLanguages;
/**
* constructor
*/
public function __construct()
{
$this->interpreterLanguages = new ArrayCollection();
}
// setters and getters omitted for brevity
}
Here is the Interpreter entity:
<?php
/** module/InterpretersOffice/src/Entity/Interpreter.php */
namespace InterpretersOffice\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
/**
* Entity representing an Interpreter.
*
* #ORM\Entity(repositoryClass="InterpretersOffice\Entity\Repository\InterpreterRepository")
* #ORM\Table(name="interpreters")
*/
class Interpreter extends Person
{
/**
* entity id.
*
* #ORM\Id #ORM\GeneratedValue #ORM\Column(type="smallint",options={"unsigned":true})
*/
protected $id;
/**
* phone number.
*
* #ORM\Column(type="string",length=16,nullable=true)
*
* #var string
*/
protected $phone;
/**
* date of birth.
*
* #ORM\Column(type="date",nullable=true)
*
* #var string
*/
protected $dob;
/**
* working languages.
*
* #ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="interpreter", cascade={"persist", "remove"})
*
*
* #var ArrayCollection of InterpreterLanguage
*/
protected $interpreterLanguages;
/**
* Constructor.
*/
public function __construct()
{
$this->interpreterLanguages = new ArrayCollection();
}
// some boring setters and getters omitted....
/**
* Add interpreterLanguage.
*
* #param InterpreterLanguage $interpreterLanguage
*
* #return Interpreter
*/
public function addInterpreterLanguage(InterpreterLanguage $interpreterLanguage)
{
$this->interpreterLanguages->add($interpreterLanguage);
return $this;
}
/**
* Remove interpreterLanguage.
*
* #param \InterpretersOffice\Entity\InterpreterLanguage $interpreterLanguage
*
* #return Interpreter
*/
public function removeInterpreterLanguage(InterpreterLanguage $interpreterLanguage)
{
$this->interpreterLanguages->removeElement($interpreterLanguage);
//$interpreterLanguage->setInterpreter(null)->setLanguage(null);
return $this;
}
/**
* Get interpreterLanguages.
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getInterpreterLanguages()
{
return $this->interpreterLanguages;
}
/*
because "AllowRemove strategy for DoctrineModule hydrator requires both addInterpreterLanguages and removeInterpreterLanguages to be defined in InterpretersOffice\Entity\Interpreter entity domain code, but one or both
[seemed] to be missing"
*/
public function addInterpreterLanguages(Collection $interpreterLanguages)
{
foreach ($interpreterLanguages as $interpreterLanguage) {
$interpreterLanguage->setInterpreter($this);
$this->interpreterLanguages->add($interpreterLanguage);
}
}
public function removeInterpreterLanguages(Collection $interpreterLanguages)
{
foreach ($interpreterLanguages as $interpreterLanguage) {
$this->interpreterLanguages->removeElement($interpreterLanguage);
}
}
}
and the association class:
/** module/InterpretersOffice/src/Entity/InterpreterLanguage.php */
namespace InterpretersOffice\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Entity representing an Interpreter's Language.
*
* Technically, it is a language *pair*, but in this system it is understood that
* the other language of the pair is English. There is a many-to-many relationship
* between interpreters and languages. But because there is also metadata to record
* about the language (federal certification), it is implemented as a Many-To-One
* relationship on either side.
*
* #ORM\Entity
* #ORM\Table(name="interpreters_languages")
*/
class InterpreterLanguage
{
/**
* constructor.
*
* #param Interpreter $interpreter
* #param Language $language
*
* #todo a lifecycle callback to ensure certified languages have a boolean
* $federalCertification set
*/
public function __construct(
Interpreter $interpreter = null,
Language $language = null
) {
if ($interpreter) {
$this->setInterpreter($interpreter);
}
if ($language) {
$this->setLanguage($language);
}
}
/**
* The Interpreter who works in this language.
*
* #ORM\ManyToOne(targetEntity="Interpreter",inversedBy="interpreterLanguages")
* #ORM\Id
*
* #var Interpreter
*/
protected $interpreter;
/**
* The language in which this interpreter works.
*
* #ORM\ManyToOne(targetEntity="Language",inversedBy="interpreterLanguages")
* #ORM\Id
*
* #var Language
*/
protected $language;
/**
* Whether the Interpreter holds federal court interpreter certification in this language.
*
* The only certified languages in the US District Court system are Spanish,
* Navajo and Haitian Creole. Of these, only the Spanish certification
* program is active. This field should be a boolean for the certified
* languages and null for everything else.
*
* #link http://www.uscourts.gov/services-forms/federal-court-interpreters/federal-court-interpreter-certification-examination the federal court certification program
*
* #ORM\Column(name="federal_certification",type="boolean",nullable=true)
*
* #var bool
*/
protected $federalCertification;
/**
* Set interpreter.
*
* #param \InterpretersOffice\Entity\Interpreter $interpreter
*
* #return InterpreterLanguage
*/
public function setInterpreter(Interpreter $interpreter = null)
{
$this->interpreter = $interpreter;
return $this;
}
/**
* Get interpreter.
*
* #return Interpreter
*/
public function getInterpreter()
{
return $this->interpreter;
}
/**
* Set language.
*
* #param Language $language
*
* #return InterpreterLanguage
*/
public function setLanguage(Language $language = null)
{
$this->language = $language;
return $this;
}
/**
* Get language.
*
* #return Language
*/
public function getLanguage()
{
return $this->language;
}
/**
* Set federalCertification.
*
* #param bool $federalCertification
*
* #return InterpreterLanguage
*/
public function setFederalCertification($federalCertification)
{
$this->federalCertification = $federalCertification;
return $this;
}
/**
* Get federalCertification.
*
* #return bool
*/
public function getFederalCertification()
{
return $this->federalCertification;
}
}
For brevity's sake, I will leave out the code for the Form and the Fieldset classes -- they do seem to be working fine (look tasteful, too. Thank you Bootstrap). I load the form, I remove one of the InterpreterLanguages and submit... Here's the controller action:
/**
* updates an Interpreter entity.
*/
public function editAction()
{
$viewModel = (new ViewModel())
->setTemplate('interpreters-office/admin/interpreters/form.phtml')
->setVariable('title', 'edit an interpreter');
$id = $this->params()->fromRoute('id');
$entity = $this->entityManager->find('InterpretersOffice\Entity\Interpreter', $id);
if (!$entity) {
return $viewModel->setVariables(['errorMessage' => "interpreter with id $id not found"]);
}
$form = new InterpreterForm($this->entityManager, ['action' => 'update']);
$form->bind($entity);
$viewModel->setVariables(['form' => $form, 'id' => $id ]);
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if (!$form->isValid()) {
return $viewModel;
}
$this->entityManager->flush();
$this->flashMessenger()
->addSuccessMessage(sprintf(
'The interpreter <strong>%s %s</strong> has been updated.',
$entity->getFirstname(),
$entity->getLastname()
));
// dump the entity and see how it looksa after update
echo "NOT redirecting. entity:<pre>";
\Doctrine\Common\Util\Debug::dump($entity); echo "</pre>";
//$this->redirect()->toRoute('interpreters');
} else {
// dump the entity fresh from the database
echo "loaded:<pre> "; \Doctrine\Common\Util\Debug::dump($entity);echo "</pre>";}
return $viewModel;
}
Again, the data looks right as it's dumped to the screen, but you reload the form and the collection has as many elements as it did before.
Thanks!
In Interpreter.php, orphanRemoval=true !!
/**
* working languages.
*
* #ORM\OneToMany(targetEntity="InterpreterLanguage",mappedBy="interpreter",
* cascade={"persist", "remove"},orphanRemoval=true)
*
* #var ArrayCollection of InterpreterLanguage
*/
protected $interpreterLanguages;
The documentation on how to retrieve joined entites as one object is very sparse, and most Stack Overflow questions on the subject are many years old.
I have a symfony project up and running with a database schema fully mapped in Doctrine. In my controllers I am able to run these two queries one after the other and they work fine.
$page = $this->getDoctrine()
->getRepository('PageBundle:SitePages')
->findByprodpageid($id);
$matrices = $this->getDoctrine()
->getRepository('PageBundle:SiteMatrices')
->findByprodpageid($id);
however both of them contain the attribute prodpageid and I would like to join the two entities on this column and receive one object containing all column values from both tables.
I am building this on top of an existing database structure so anything to do with changing the database structure etc is out of the question.
I have added annotations in my entities to specify which columns should be joined, in a ManyToOne relationship. But how do I activate that relation and receive the joined object?
Thanks for any info on the subject.
EDIT: Here are my relationships from the entities
//Entities/SitePages
/**
* #var integer
*
* #ORM\Column(name="ProdPageID", type="smallint")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*
* #ORM\OneToMany(targetEntity="SiteMatrices")
* #ORM\JoinColumn(name="prodpageid", referencedColumnName="prodpageid")
*/
private $prodpageid;
//Entities/SiteMatrices
/**
* #var integer
*
* #ORM\Column(name="ProdPageID", type="smallint", nullable=false)
* #ORM\ManyToOne(targetEntity="SitePages")
* #ORM\JoinColumn(name="prodpageid", referencedColumnName="prodpageid")
*
*/
private $prodpageid;
You are saying that a Page has many Matrices. I will make some changes by your permission in mapping annotations:
/**
* Entities/SitePages
*
* #var integer
*
* #ORM\Column(name="ProdPageID", type="smallint")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*
* #ORM\OneToMany(targetEntity="SiteMatrices")
* #ORM\JoinColumn(name="prodpageid", referencedColumnName="prodpageid")
*/
private $matrices;
/**
* #return ArrayCollection
*/
public function getMatrices(){
return $this->matrices;
}
/**
* #param Entities/SiteMatrices[]
* #return $this
*/
public function setMatrices($matrices){
$this->matrices = $matrices;
return $this;
}
and
/**
* Entities/SiteMatrices
*
* #var integer
*
* #ORM\Column(name="ProdPageID", type="smallint", nullable=false)
* #ORM\ManyToOne(targetEntity="SitePages")
* #ORM\JoinColumn(name="prodpageid", referencedColumnName="prodpageid")
*
*/
private $page;
/**
* #return Entities/SitePages
*/
public function getPage(){
return $this->page;
}
/**
* #param Entities/SitePages
* #return $this
*/
public function setPage($page){
$this->page = $page;
return $this;
}
Now if you query the Pages with this DQL:
$pages = $this->getDoctrine()
->getRepository('PageBundle:SitePages')
->findByprodpageid($id);
Then you could get each page's matrices simply by traversing on matrices association:
foreach($pages as $page){
$matrices = $page->getMatrices(); // will give you an ArrayCollection of all matrices objects joined by prodpageid to this page.
}
Hope I did not get you wrong and it helps.
Even if the object is correctly updated it seems that the data is not persisted and I don't get it why.
Here is my Entity:
Article.php
/**
* #var AttributeInArticle
*
* #ORM\OneToMany(
* targetEntity="XXX\DatabaseBundle\Entity\AttributeInArticle",
* mappedBy="article"
* )
*/
private $attributeInArticles;
....
public function __construct()
{
$this->attributeInArticles = new ArrayCollection();
}
/**
* #return AttributeInArticle
*/
public function getAttributeInArticles()
{
return $this->attributeInArticles;
}
public function addAttributeInArticle(AttributeInArticle $attributeInArticles)
{
$this->attributeInArticles[] = $attributeInArticles;
return $this;
}
public function removeAttributeInArticle(AttributeInArticle $attributeInArticles)
{
$this->attributeInArticles->removeElement($attributeInArticles);
}
AttributeInArticle.php
/**
* #var Attribute
*
* #ORM\ManyToOne(
* targetEntity="XXX\DatabaseBundle\Entity\Attribute",
* inversedBy="attributeInArticles"
* )
* #ORM\JoinColumns({
* #ORM\JoinColumn(
* name="attribute_id",
* referencedColumnName="id")
* })
*/
private $attribute;
/**
* #var Article
*
* #ORM\ManyToOne(
* targetEntity="XXX\DatabaseBundle\Entity\Article",
* inversedBy="attributeInArticles"
* )
* #ORM\JoinColumns({
* #ORM\JoinColumn(
* name="article_id",
* referencedColumnName="id"
* )
* })
*
*/
private $article;
/**
* #return Attribute
*/
public function getAttribute()
{
return $this->attribute;
}
/**
* #param Attribute $attribute
*/
public function setAttribute($attribute)
{
$this->attribute = $attribute;
}
+ getter & setter for $article
And in the controller I am calling it like this:
$article->removeAttributeInArticle($attributeInArticle);
If I dumpthe $article object before and after the remove action the $article object has the corect data in it. That means that $attributeInArticle was removed.
But for some reason it does not persist this data.
The answer is easy here.
You are removing an Attribute from an Article and the persisting/flushing Article. If you look to your mappings you'll notice easily that the relationship between Article and AttributeInArticole is owned by the latter.
When you do flushing operations, doctrine, for performance reasons, "looks" only to owning side of a relationship and for changes in it: if nothing changed there's no need to write it in db.
So, what you can do here, is to remove the $attributeInArticle directly without worry about Article entity.
So, basically
$em = $this
->getDoctrine()
->getManager();
$em->remove($attributeInArticle);
$em->flush();
Another option is to use orphanRemoval on $attribueInArticles of your Article entity.
So, basically,
//Article.php
/**
* #var AttributeInArticle
*
* #ORM\OneToMany(
* targetEntity="XXX\DatabaseBundle\Entity\AttributeInArticle",
* mappedBy="article",
* orphanRemoval=true
* )
*/
private $attributeInArticles;
and
$article->removeAttributeInArticle($attributeInArticle);
You need cascade={"remove"}
/**
* #var AttributeInArticle
*
* #ORM\OneToMany(
* targetEntity="XXX\DatabaseBundle\Entity\AttributeInArticle",
* mappedBy="article",
* cascade={"remove"}
* )
*/
Read doctrine documentation: 8. Working with Associations. Also check "Orphan Removal"
I have recently startet with Zend Framework 2 and came now across Doctrine 2, which I would now like to integrate in my first project.
I have now got the following situation and even after days, I can not find a solution.
I have 3 Tables:
Advert
advert_id
advert_title
etc
Category
category_id
name
label
etc
advert2category
advert2category_category_id
advert2category_advert_id
An Advert can be in different Categories and different Categories have different Adverts, therefore the table Advert2Category (ManytoMany).
After reading through the www, I have decided that it should be a ManytoMany Bidirectional, with the "owning side" at the Advert Entity.
Don't ask me why I decided that, I still don't understand Doctrine fully. Anyway, I created 3 Entities, but guess I only need Advert and Category Entity.
I now want the following to happen.
I click on a Category and want to see a list of Articles within this category., that means I have to read out the Table advert2category. I have created the Entities, here my Advert Entity:
So here is first my Advert Entity:
namespace Advert\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Advert
*
* #ORM\Table(name="advert")
* #ORM\Entity
*/
class Advert
{
/**
* #var integer
*
* #ORM\Column(name="advert_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $advertId;
/**
* #var string
*
* #ORM\Column(name="advert_title", type="string", length=255, nullable=true)
*/
private $advertTitle;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="advertCategory", cascade={"persist"})
* #ORM\JoinTable(name="advert2category",
* joinColumns={#ORM\JoinColumn(name="advert2category_category_id", referencedColumnName="category_id")},
* inverseJoinColumns={#ORM\JoinColumn(name="advert2category_advert_id", referencedColumnName="advert_id")}
* )
*/
protected $category;
public function __construct()
{
$this->category = new ArrayCollection();
}
/**
* Get advertId
*
* #return integer
*/
public function getAdvertId()
{
return $this->advertId;
}
/**
* Set advertTitle
*
* #param string $advertTitle
* #return Advert
*/
public function setAdvertTitle($advertTitle)
{
$this->advertTitle = $advertTitle;
return $this;
}
/**
* Get advertTitle
*
* #return string
*/
public function getAdvertTitle()
{
return $this->advertTitle;
}
/**
* Set category
*
* #param \Advert\Entity\User $category
* #return Advert
*/
public function setCategory(\Advert\Entity\Category $category = null)
{
$this->category = $category;
return $this;
}
/**
* Get category
*
* #return \Advert\Entity\Category
*/
public function getCategory()
{
return $this->category;
}
}
And my Category Entity:
namespace Advert\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Category
*
* #ORM\Table(name="category")
* #ORM\Entity
*/
class Category
{
/**
* #var integer
*
* #ORM\Column(name="category_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $categoryId;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="Advert", mappedBy="category")
**/
private $advertCategory;
public function __construct()
{
$this->advertCategory = new ArrayCollection();
}
/**
* Get categoryId
*
* #return integer
*/
public function getCategoryId()
{
return $this->categoryId;
}
/**
* 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;
}
}
Just as a first test, I have now tried the following in my Controller:
//Below Controller now works to echo the categories ArrayCollection
$data = $this->getEntityManager()->getRepository('Advert\Entity\Advert')->findAll();
foreach($data as $key=>$row)
{
echo $row->getAdvertTitle();
echo $row->getUser()->getUsername();
$categories = $row->getCategory();
foreach($categories as $row2) {
echo $row2->getName();
}
What am I doing wrong here? Can anyone give me an advice? Thank you very much in advance !
Honestly, and it's a very honest and fine thing, that this is way overcomplicating what you want to do, but only in specific areas.
If you used Composer to include Doctrine (the recommended way), also include symfony/console and you will get a whole mess of awesome tools to help you on your quest. There is a very specific command that will kick you in your seat for how awesome it is: $ doctrine orm:schema-tool:update --force --dump-sql. This will get Doctrine to run through your Entities (you only need the two) and will generate your tables and even setup the *To* associations for you. Int he case of ManyToOne's it will generate the appropriate Foreign Key schema. In the case of ManyToMany's it will automatically create, AND manage it's own association table, you just need only worry about giving the table a name in the Entity.
I'm not kidding you, Do this. It will make your life worth living.
As for your entity setup, this is all you need:
<?php
namespace Advert\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Advert
*
* #ORM\Table(name="advert")
* #ORM\Entity
*/
class Advert
{
/**
* #var integer
*
* #ORM\Column(name="advert_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $advertId;
/**
* #var string
*
* #ORM\Column(name="advert_title", type="string", length=255, nullable=true)
*/
private $advertTitle;
/**
* #ORM\ManyToMany(targetEntity="Category", cascade={"persist"})
* #JoinTable(name="advert_categories")
*/
protected $category;
public function __construct()
{
$this->category = new ArrayCollection();
}
/**
* Get advertId
*
* #return integer
*/
public function getAdvertId()
{
return $this->advertId;
}
/**
* Set advertTitle
*
* #param string $advertTitle
* #return Advert
*/
public function setAdvertTitle($advertTitle)
{
$this->advertTitle = $advertTitle;
return $this;
}
/**
* Get advertTitle
*
* #return string
*/
public function getAdvertTitle()
{
return $this->advertTitle;
}
/**
* Set category
*
* #param ArrayCollection $category
* #return Advert
*/
public function setCategory(ArrayCollection $category)
{
$this->category = $category;
return $this;
}
/**
* Get category
*
* #return ArrayCollection
*/
public function getCategory()
{
return $this->category;
}
}
Notice that the getters and setters are Documented to Set and Return ArrayCollection, this is important for IDE's and tools that read PHPDoc and Annotations to understand how in-depth PHP class mapping works.
In addition, notice how much simpler the ManyToMany declaration is? The #JoinTable annotation is there to give a name to the table that doctrine will generate and manage. That's all you need!
But now, you probably should remove the $advertCategory property out of the Category Entity. Doctrine is going to auto-hydrate embedded Entities in properties with the Entity Association Mappings.
This is also potentially dangerous as it can result in infinite recursion. Basically, if all you requested was an Advert with ID of 1, it would go in and find ALL of the Category Entities associated to Advert 1, but inside of those Categories it's re-referencing Advert 1, which Doctrine will sub-query for and inject, which will contain a Category association, which will then Grab those categories, and so on and so fourth until PHP kills itself from lack of memory.
Once everything is good to go, and you got some Categories associated with your Advert, using the Getter for your category in the Advert entity will return an array of Category Entities. Simply iterate through them:
foreach($category as $advert->getCategories()) {
echo $category->getName();
}
or
echo current($advert->getCategories())->getName();
After I successfuly created TaskBundle with One-to-Many relation between category and tasks, now I'm trying to create a new TaskBundle with Many-to-Many relation. I get also problem with checking checkbox in this relation, but now it is not a primary problem (maybe after solving this). I deleted all tables, which is TaskBundle using and trying to create a new, but here is problem (description at the bottom).
My Task object:
<?php
namespace Acme\TaskBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="tasks")
*/
class Task
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=200)
* #Assert\NotBlank(
* message = "Task is empty"
* )
* #Assert\Length(
* min = "3",
* minMessage = "Task is too short"
* )
*/
protected $task;
/**
* #ORM\Column(type="datetime")
* #Assert\NotBlank()
* #Assert\Type("\DateTime")
*/
protected $dueDate;
/**
* #Assert\True(message = "You have to agree.")
*/
protected $accepted;
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
* #ORM\JoinTable(name="categories")
*/
protected $category;
/**
* Constructor
*/
public function __construct()
{
$this->category = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set task
*
* #param string $task
* #return Task
*/
public function setTask($task)
{
$this->task = $task;
return $this;
}
/**
* Get task
*
* #return string
*/
public function getTask()
{
return $this->task;
}
/**
* Set dueDate
*
* #param \DateTime $dueDate
* #return Task
*/
public function setDueDate($dueDate)
{
$this->dueDate = $dueDate;
return $this;
}
/**
* Get dueDate
*
* #return \DateTime
*/
public function getDueDate()
{
return $this->dueDate;
}
/**
* Add category
*
* #param \Acme\TaskBundle\Entity\Category $category
* #return Task
*/
public function addCategory(\Acme\TaskBundle\Entity\Category $category)
{
$this->category[] = $category;
return $this;
}
/**
* Remove category
*
* #param \Acme\TaskBundle\Entity\Category $category
*/
public function removeCategory(\Acme\TaskBundle\Entity\Category $category)
{
$this->category->removeElement($category);
}
/**
* Get category
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategory()
{
return $this->category;
}
}
and Category object
<?php
namespace Acme\TaskBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="categories")
*/
class Category
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=200, unique=true)
* #Assert\NotNull(message="Categories cannot be empty", groups = {"adding"})
*/
protected $name;
/**
* #ORM\ManyToMany(targetEntity="Task", mappedBy="category")
*/
private $tasks;
public function __toString()
{
return strval($this->name);
}
/**
* Constructor
*/
public function __construct()
{
$this->tasks = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return 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;
}
/**
* Add tasks
*
* #param \Acme\TaskBundle\Entity\Task $tasks
* #return Category
*/
public function addTask(\Acme\TaskBundle\Entity\Task $tasks)
{
$this->tasks[] = $tasks;
return $this;
}
/**
* Remove tasks
*
* #param \Acme\TaskBundle\Entity\Task $tasks
*/
public function removeTask(\Acme\TaskBundle\Entity\Task $tasks)
{
$this->tasks->removeElement($tasks);
}
/**
* Get tasks
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTasks()
{
return $this->tasks;
}
}
So, after i put doctrine:schema:update --force i'll get error: Table 'symfony.categories' already exists. I've tried to delete all caches, but same problem. Any idea?
There's only problem, if it is as m2m relation.
PS: I was looking for this problem at the Google, but no one answers at this problem. There were only questions, but not correct answers, where the problem is and how to solve it.
Looks like you already have table named "categories" in that database. Remove this line #ORM\JoinTable(name="categories") and try without it.
P.S. "Categories" is really a strange name for join table. You should probably follow some conventions and let doctrine name it. Common names for join tables are category_task or category2task as they are more self-explanatory. Nothing that important, just trying to suggest what I consider good practice.
The thing is that doctrine doesn't understand how your existing table should be used. But you can give him some help.
You have two options :
You don't care about the existing table : simple, you can remove the #ORM\JoinTable(name="categories") annotation, and doctrine will create an other table etc.
You want to keep your existing table, which sounds pretty logical : you have to be more explicit in your annotation by adding #ORM\JoinColumn annotation.
Here is an example:
class
<?php
...
/**
* #ORM\Entity
* #ORM\Table(name="tasks")
*/
class Task
{
...
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
* #ORM\JoinTable(name="categories",
* joinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="task_id", referencedColumnName="id")})
*/
protected $category;
...
}
and Category object
<?php
...
/**
* #ORM\Entity
* #ORM\Table(name="categories")
*/
class Category
{
...
/**
* #ORM\ManyToMany(targetEntity="Task", mappedBy="category")
* #ORM\JoinTable(name="categories",
* joinColumns={#ORM\JoinColumn(name="task_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")})
*/
private $tasks;
...
Doing so, you will be able to keep your table without any doctrine error.
My fix for this, as far as I can tell, was a case-sensitivity issue with table names. Doctrine let me create a Users and a users table but afterwards would die on migrations:diff or migrations:migrate .
I used the -vvv option to get more detail on this error message; it seems that the error happens when Doctrine is loading up its own internal representation of the current database's schema. So if your current database has table names that Doctrine doesn't understand (like two tables that are identical, case-insensitive) then it will blow up in this fashion.
Seems like most of the answers above assume that the error is in your code, but in my case it was in the database.
I got this error with 2 ManyToMany targeting the same entity (User in the exemple below).
To create the table name doctrine use the entity and target entity name.
So in my case it was trying to create two time the table thread_user
To debug this it's easy. Just use the '#ORM\JoinTable' annotation and specify the table name.
Here is a working exemple.
/**
* #ORM\ManyToMany(targetEntity="App\Entity\User")
* #ORM\JoinTable(name="thread_participant")
*/
private $participants;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\User")
* #ORM\JoinTable(name="thread_recipient")
*/
private $recipients;
in Symfony4.1 you can force the migration using the migration version
doctrine:migrations:execute <migration version>
ex
for migration version123456.php use
doctrine:migrations:execute 123456
there is another using the table name ,you can search it in your project . Maby be demo,I think it...
sorry for my chinese english !
Try to drop everything inside of your proxy directory.
I fix same issue after check other entities on each bundles, be aware of this.