Hi i am trying to create a OneToMany relationship between Page and Block tables but i am getting the following error on validating the schema:
[Mapping] FAIL - The entity-class 'mypath\Entity\Block' mapping is invalid:
* The association mypath\Entity\Block#pages refers to the inverse side field mypath\Entity\Page#blocks which does not exist.
[Mapping] FAIL - The entity-class 'mypath\Entity\Page' mapping is invalid:
* The association mypath\Entity\Page#block refers to the owning side field mypath\Entity\Block#page which does not exist.
Following are my page and Block Entities
Page:
/**
* #ORM\OneToMany(targetEntity="Block", mappedBy="page")
*/
private $block;
Block:
/**
* #ORM\ManyToOne(targetEntity="Page", inversedBy="block")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $pages;
I am not sure what wrong with it but seems to be something related to annotations. Please help, Thanks !!!
Ok i found the solution. The problem was with the names.
So if we read it in normal terms, it make sense.
A page can have many blocks. So page entity will be
/**
* #ORM\OneToMany(targetEntity="Block", mappedBy="pages")
*/
private $blocks;
Similarly, a block belongs to many pages. So block entity will be
/**
* #ORM\ManyToOne(targetEntity="Page", inversedBy="blocks")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $pages;
targetEntity will always be the class name which would be singular and field names will be plural(in case of OneToMany and may be in other cases as well).
That resolves the issue. Have a good day !!!
In the annotation, you call the properties "blocks and "page", but they're called "block" and "pages" in the actual code.
Page:
/**
* #ORM\OneToMany(targetEntity="Block", mappedBy="page")
*/
private $blocks;
Block:
/**
* #ORM\ManyToOne(targetEntity="Page", inversedBy="blocks")
*/
private $page;
Related
I'm working on a OneToOne join in doctrine2/symphony 2.8.2 and I keep getting:
The association X\BaseDesignBundle\Entity\SessionDesign#user refers to the inverse side field X\UserBundle\Entity\User#SessionDesign which does not exist.
User:
/**
* #ORM\OneToOne(targetEntity="X\BaseDesignBundle\Entity\SessionDesign")
* #ORM\JoinColumn(name="fcid", referencedColumnName="id")
*/
private $sessionDesign;
Session Design:
/**
* #ORM\OneToOne(targetEntity="x\UserBundle\Entity\User")
* #ORM\JoinColumn(name="id", referencedColumnName="fcid")
*/
private $user;
I have 0 idea whats wrong at this point and I have tried everything I can think of. Thanks for any help you can give.
JoinColumn should only appear on the entity where the FK is.
E.g (assuming FK is on Session Design.)
Session Design
/**
* #ORM\OneToOne(targetEntity="X\BaseDesignBundle\Entity\User")
* #ORM\JoinColumn(name="fcid", referencedColumnName="id")
*/
private $user;
User:
/**
* #ORM\OneToOne(targetEntity="x\UserBundle\Entity\SessionDesign", inversedBy="user")
*/
private $sessionDesign;
For your own sanity I would recommend naming your FK better. fcid doesn't mean much at a glance, why not call it user_id or session_design_id (depending on where you put the FK).
E.g. * #ORM\JoinColumn(name="session_design_id", referencedColumnName="id")
It just makes things easier to parse
See the documentation for more info.
I would add to previous answer:
#ORM\OneToOne(targetEntity="X\BaseDesignBundle\Entity\User", mappedBy="sessionDesign")
I went through a strange "bug".
I have 2 associations on an entity with "almost the same same" :
/**
* #ORM\OneToOne(targetEntity="TaxeApprentissage\Entity\Collecteur\Parametres", mappedBy="collecteur")
*/
private $parametres;
/**
* #ORM\OneToOne(targetEntity="TaxeApprentissage\Entity\Collecteur\ParametresEdition", mappedBy="collecteur")
*/
private $parametresEdition;
When lazy loading happens, I got the exact same object TaxeApprentissage\Entity\Collecteur\ParametresEdition in both properties $parametres and $parametresEdition.
But when I reverse the associations :
/**
* #ORM\OneToOne(targetEntity="TaxeApprentissage\Entity\Collecteur\ParametresEdition", mappedBy="collecteur")
*/
private $parametresEdition;
/**
* #ORM\OneToOne(targetEntity="TaxeApprentissage\Entity\Collecteur\Parametres", mappedBy="collecteur")
*/
private $parametres;
Everything is working perfectly. I get the correct object for each association.
Is it caused by the fact that the entities are named almost similar?
It is probably because you made a mistake on the owning side? What do the column definitions on other side (inside Parametres and ParametresEdition) look like? You don't show them in your question.
You probably have twice inversedBy="parametres" or inversedBy="parametresEdition".
#ORM\OneToOne(targetEntity="TaxeApprentissage\Entity\Collecteur\Collecteur", inversedBy="????")
#ORM\OneToOne(targetEntity="TaxeApprentissage\Entity\Collecteur\Collecteur", inversedBy="????")
I have problem with Doctrine mapping. First of all I'll introduce my two entites:
/**
* #ORM\Entity
* #ORM\Table(name="header_fotos")
*/
class HeaderFoto extends BaseEntity{
....
/**
*
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="\App\Webpage\Webpage", inversedBy="headerFotos")
* #ORM\JoinColumn(name="webpage_id", referencedColumnName="id")
*/
protected $webpage;
...
}
Second entity:
/**
* #ORM\Entity
* #ORM\Table(name="webpages")
*/
class Webpage extends BaseEntity{
...
/**
* #ORM\OneToMany(targetEntity="\App\Webpage\HeaderFoto", mappedBy="webpage")
*/
protected $headerFotos;
public function __construct() {
parent::__construct();
$this->headerFotos = new ArrayCollection();
}
My problem is with mapping. When I load entity Webpage and try to access all entities of type HeaderFoto it cannot find any relation. I was trying to compare with another working project with Doctrine and everything is the same.
I was trying to change association to OneToOne on the both sides, just for sure. But in this case it returned Exception No mapping found for field webpage.
I will appreciate every help and advice. Thanks for help.
EDIT
To be more specific, it has problem in class \Doctrine\ORM\Persisters\BasicEntityPersister.php in method getOneToManyStatement. It tries to load associated objects, but the array associationMappings in this class is empty. This is the last thing what I have found out.
I have a View entity that represents the primary page record, and then I have an associated entity called ViewVersion which stores multiple versions of the entity as it's changed over time. The View entity sets the current "Published" ViewVersion in the VersionId field. This makes for a simple OneToOne association. But in some contexts I will also want to get all the versions associated with this View entity, e.g. if I want to allow the user to review older versions and revert back. So I will need another mapping which is a OneToMany. The first viewVersion will map to the active "published" version, and the second viewVersions will show all the versions.
Entity Definitions
/**
* #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 $viewVersion;
/**
* #ORM\Column(type="integer", nullable=true)
*/
protected $versionId = NULL;
/**
* #ORM\OneToMany(targetEntity="\Gutensite\CmsBundle\Entity\View\ViewVersion", mappedBy="viewAll", cascade={"persist", "remove"}, orphanRemoval=true)
*/
protected $viewVersions;
}
/**
* #ORM\Entity
* #ORM\Table(name="view_version")
* #ORM\Entity(repositoryClass="Gutensite\CmsBundle\Entity\View\ViewVersionRepository")
*/
class ViewVersion extends Entity\Base {
/**
* #ORM\OneToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\View", mappedBy="viewVersion", cascade={"persist"})
*/
protected $view;
/**
* #ORM\ManyToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\View", inversedBy="viewVersions")
* #ORM\JoinColumn(name="viewId", referencedColumnName="id")
*/
protected $viewAll;
/**
* The primary view entity that this version belongs to.
* #ORM\Column(type="integer", nullable=true)
*/
protected $viewId;
}
This "works" but is it recommended to have two associations with the same entity like this? Or is this a really bad idea?
The ViewVersion entity will reference a single View entity in both cases, but the mapped associations need two separate variables, e.g. View and ViewAll. I'm not exactly sure how the internals work for the association, and how the reference variable with the mapping is used.
Alternatively, I could get rid of the OneToOne association, and just set a ViewRepository function to get the current published version based on the versionId (just like the old mapped entity used to do with the getVersion()). That would work, but is it more internal overhead, because it would make two queries... or will Doctrine be smart enough to optimize this, just like it did with the getVersion().
NOTE:
These other answers are not complete.
References:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html
http://doctrine-orm.readthedocs.org/en/2.0.x/reference/association-mapping.html#one-to-many-bidirectional
Typically, I have found the best approach is to solve this in a different way.
One common pattern I have seen before is you use a single table to hold all records, and have an 'active' flag.
If your query to select the active one works like so:
SELECT * FROM table WHERE active = true ORDER BY updated_at DESC LIMIT 1;
Then enabling a new one becomes as simple as:
UPDATE table SET active = 1, updated_at = '<timestamp>' WHERE id = <new id>;
UPDATE table SET active = 0, updated_at = '<timestamp>' WHERE id = <old id>;
Your new page will be active as soon as the first query hits, and your second query will avoid any sort of weirdness as that row will already be no longer active.
If you have other models that depend on a consistent ID to reference, then another route which also maintains some sanity would be to have one table for the active entries (in whole, not in part) and then a second table with additional metadata to track versions.
The latter approach could be nicely handled via Doctrine's inheritance system (http://docs.doctrine-project.org/en/2.0.x/reference/inheritance-mapping.html) which would let you define the base View class, and then for the "ViewRevision" model, extend View and add a "Revised on" type timestamp.
Per the advice from #jmather I've decided this model is "okay", because I need a single View entity that other entities can access (e.g. Routing urls that point to a single View, i.e. "page").
I've changed the OneToOne relationship for View to be unidirectional only, because the ViewVersion already has an association back to the View via the other OneToMany (so it doesn't need two paths back).
This allows me to keep a simple method for $view->getPublished() handy and seems more logical.
/**
* #ORM\Entity
* #ORM\Table(name="view")
*/
class View extends Entity\Base {
/**
* This is a OneToOne Unidirectional association, just so that we can get the
* current published version easily, based on the publishedId.
* #ORM\OneToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\TestVersion")
* #ORM\JoinColumn(name="publishedId", referencedColumnName="id")
*/
protected $published;
/**
* #ORM\Column(type="integer", nullable=true)
*/
protected $publishedId = NULL;
/**
* This is the regular OneToMany Bi-Directional Association, for all the versions.
* #ORM\OneToMany(targetEntity="\Gutensite\CmsBundle\Entity\View\ViewVersion", mappedBy="view", cascade={"persist", "remove"}, orphanRemoval=true)
*/
protected $versions;
}
/**
* #ORM\Entity
* #ORM\Table(name="view_version")
*/
class ViewVersion extends Entity\Base {
/**
* #ORM\ManyToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\View", inversedBy="versions")
* #ORM\JoinColumn(name="viewId", referencedColumnName="id")
*/
protected $view;
/**
* The primary view entity that this version belongs to.
* #ORM\Column(type="integer", nullable=true)
*/
protected $viewId;
}
However, I've discovered that as long as the $view->publishedId is set the view can't be deleted from the database because of foreign key constraints (even though it's uni-directional). So I have to break that foreign key link before removing. I think that's fine. I posted details about that here: Overlapping Entity Association causing Database Foreign Key Constraint Errors when Removing Entity
I am developing my application using Zend Framework 2 and Doctrine 2.
While writting annotations, I am unable to understand the difference between mappedBy and inversedBy.
When should I use mappedBy?
When should I use inversedBy?
When should I use neither?
Here is an example:
/**
*
* #ORM\OneToOne(targetEntity="\custMod\Entity\Person", mappedBy="customer")
* #ORM\JoinColumn(name="personID", referencedColumnName="id")
*/
protected $person;
/**
*
* #ORM\OneToOne(targetEntity="\Auth\Entity\User")
* #ORM\JoinColumn(name="userID", referencedColumnName="id")
*/
protected $user;
/**
*
* #ORM\ManyToOne (targetEntity="\custMod\Entity\Company", inversedBy="customer")
* #ORM\JoinColumn (name="companyID", referencedColumnName="id")
*/
protected $company;
I did a quick search and found the following, but I am still confused:
example 1
example 2
example 3
mappedBy has to be specified on the inversed side of a (bidirectional) association
inversedBy has to be specified on the owning side of a (bidirectional) association
from doctrine documentation:
ManyToOne is always the owning side of a bidirectional assocation.
OneToMany is always the inverse side of a bidirectional assocation.
The owning side of a OneToOne assocation is the entity with the table containing the foreign key.
See https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html
The answers above were not sufficient for me to understand what was going on, so after delving into it more I think I have a way of explaining it that will make sense for people who struggled like I did to understand.
inversedBy and mappedBy are used by the INTERNAL DOCTRINE engine to reduce the number of SQL queries it has to do to get the information you need. To be clear if you don't add inversedBy or mappedBy your code will still work but will not be optimized.
So for example, look at the classes below:
class Task
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="task", type="string", length=255)
*/
private $task;
/**
* #var \DateTime
*
* #ORM\Column(name="dueDate", type="datetime")
*/
private $dueDate;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="tasks", cascade={"persist"})
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
}
class Category
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="Task", mappedBy="category")
*/
protected $tasks;
}
These classes if you were to run the command to generate the schema (for example, bin/console doctrine:schema:update --force --dump-sql) you will notice that the Category table does not have a column on it for tasks. (this is because it does not have a column annotation on it)
The important thing to understand here is that the variable tasks is only there so the internal doctrine engine can use the reference above it which says its mappedBy Category. Now... don't be confused here like I was... Category is NOT referring TO THE CLASS NAME, its referring to the property on the Task class called 'protected $category'.
Like wise, on the Tasks class the property $category mentions it is inversedBy="tasks", notice this is plural, this is NOT THE PLURAL OF THE CLASS NAME, but just because the property is called 'protected $tasks' in the Category class.
Once you understand this it becomes very easy to understand what inversedBy and mappedBy are doing and how to use them in this situation.
The side that is referencing the foreign key like 'tasks' in my example always gets the inversedBy attribute because it needs to know what class (via the targetEntity command) and what variable (inversedBy=) on that class to 'work backwards' so to speak and get the category information from. An easy way to remember this, is the class that would have the foreignkey_id is the one that needs to have inversedBy.
Where as with category, and its $tasks property (which is not on the table remember, just only part of the class for optimization purposes) is MappedBy 'tasks', this creates the relationship officially between the two entities so that doctrine can now safely use JOIN SQL statements instead of two separate SELECT statements. Without mappedBy, the doctrine engine would not know from the JOIN statement it will create what variable in the class 'Task' to put the category information.
Hope this explains it a bit better.
In bidirectional relationship has both an owning side and an inverse side
mappedBy : put into The inverse side of a bidirectional relationship To refer to the field in the owning side of entity
inversedBy : put into The owning side of a bidirectional relationship To refer to the field on the inverse side of entity
AND
mappedBy attribute used with the OneToOne, OneToMany, or ManyToMany mapping declaration.
inversedBy attribute used with the OneToOne, ManyToOne, or ManyToMany mapping declaration.
Notice :
The owning side of a bidirectional relationship the side that contains the foreign key.
there two reference about inversedBy and mappedBy into Doctrine Documentation :
First Link,Second Link
5.9.1. Owning and Inverse Side
For Many-To-Many associations you can chose which entity is the owning and which the inverse side. There is a very simple semantic rule to decide which side is more suitable to be the owning side from a developers perspective. You only have to ask yourself, which entity is responsible for the connection management and pick that as the owning side.
Take an example of two entities Article and Tag. Whenever you want to connect an Article to a Tag and vice-versa, it is mostly the Article that is responsible for this relation. Whenever you add a new article, you want to connect it with existing or new tags. Your create Article form will probably support this notion and allow to specify the tags directly. This is why you should pick the Article as owning side, as it makes the code more understandable:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html