o-o!
I am trying to map the wp_posts table from WordPress database. I have left out information to simplify my question:
This is the entity:
#Entity
#Table(name = "wp_posts")
#Getter
#Setter
public class WPPosts {
#Id
#Column(name = "ID")
private long id;
#Column(name = "post_parent")
private long postParent;
#OneToMany(targetEntity = WPPosts.class, mappedBy = "postParent")
List<WPPosts> childPosts;
}
An example for the table could be (again: simplified!):
id --- post_parent
1 ---- null
2 ---- 1
3 ---- 1
4 ---- null
So entity with id 1 should get a list childPosts of size 2 (containing post 2 and 3).
Unfortunately this does not work. I do not get any exception, but the list stays empty.
mappedBy is supposed to be referring to an entity, not an id. You need a #ManyToOne WPPosts postParent field for your mapping to work.
Note that with such a mapping, WPPosts.postParent is the owning side of the relationship, meaning that only changes to WPPosts.postParent will be honored by JPA; changes to WPPosts.childPosts will be ignored.
As far as accessing the list goes, you'll want to ensure to either only access the list within a transaction context, or declare the #OneToMany as fetch=FetchType.EAGER. Otherwise, you'll get lazy initialization exceptions.
Related
If entity A contains multiple entity B and has cascade:persist, how to reuse existing entities B when persisting ?
B entity has one primary key, an integer, and the id of the A parent. The only data it contains is the primary key.
Example:
A has 2 B entities, identified by their id, 14 and 23.
A.Bs = [{id=14, AId=A.id}, {id=23, AId=A.Id}]
Now if I modify this managed entity, to add a B entity to A, with id = 56.
A.Bs = [{id=14, AId=A.id}, {id=23, AId=A.Id}, {id=56}]
Relationships
Entity A
/**
* #var B[]|ArrayCollection
*
* #ORM\OneToMany(targetEntity="B", mappedBy="A", cascade={"persist", "remove"}, orphanRemoval=true)
* #Assert\Valid
*/
private $Bs;
Entity B
/**
* #var A
*
* #ORM\ManyToOne(targetEntity="A", inversedBy="Bs")
* #ORM\JoinColumn(name="A_id", referencedColumnName="A_id")
* #Assert\NotNull()
*/
private $A;
If I try to persist I get Integrity constraint violation, because Doctrine tries to persist the existing entities, that have id 14 and 23.
I understand this is expected behaviour, but how can I make it persist new entities, and reuse existing ones ?
More details:
If I get an existing entity A with $em->find($id) and directly use persist and flush, I will get UniqueConstraintException because it tries to persist the already persisted B entities.
Example code:
/** #var A $existingEntityA */
$existingEntityA = $this->getEntity($id);
$this->serializerFactory->getComplexEntityDeserializer()->deserialize(json_encode($editedEntityADataJson), A::class, 'json', ['object_to_populate' => $existingEntityA]);
$this->entityValidator->validateEntity($existingEntityA);
$this->_em->flush();
Example error : Integrity constraint violation: 1062 Duplicate entry '777111' for key 'PRIMARY'
If I understand your example properly - you're doing something like this:
$b = new B();
$b->setId(56);
$a->getB()->add($b);
and you having a row with primary key 56 into database table that is represented by B?
If my assumption is correct - it is wrong way to go. Reason is that Doctrine internally stores so called "identity map" that keeps track of all entities that either being fetched from database or persisted by calling EntityManager::persist(). Every entity that is scheduled for commit but not available into identity map is considered as "new" and scheduled for insertion. If row with same primary key is already available in database - you're receiving UniqueConstraintException.
Doctrine doesn't handle a case "let me look if there is an entity with such primary key in database" by itself because it will hurt performance significantly and is not needed in most cases. Each such test will result into database query, imagine if you will have thousands of such entities. Since Doctrine doesn't know business logic of your application - it will spend even more resources with attempts to guess optimal strategy so this is intentionally left out of scope.
Correct way for you would be to get your entity by itself before adding to collection:
$newB = $em->find(B::class, 56);
if ($newB) {
$a->getB()->add($newB);
}
In this case new entity will internally have "managed" status and will be correctly handled by Doctrine at a time of commit.
I have a many-to-many relationship this is my table
site
------
id
name
site_landingpage
---------------
id
site_id
landingpage_id
landingpage
----------
id
name
Page.php
-----------------------
/**
* #ORM\ManyToMany(targetEntity="Page\DefaultBundle\Entity\Site", mappedBy="landingpages")
**/
private $sites;
Site.php
/**
* #ORM\ManyToMany(targetEntity="Site\PageBundle\Entity\Page", inversedBy="sites")
* #ORM\JoinTable(name="site_landingpage")
**/
private $landingpage;
If I add a landingpage it should get the current site and populate site_landingpage table how am I able to do this in the controller part where you add a landingpage given that my site_id is $site_id
I'm not sure if your entities have their getters & setters created, if not generate them by running:
app/console doctrine:generate:entities PageBundle:Site
app/console doctrine:generate:entities PageBundle:Page
Then you can simply do something like:
$landingpage = new Page();
$site->addLandingpage($landingpage);
This blog post by Kontroversial Keith provides a detailed example of populating many-to-many relationship entities from user input.
As a side note, join tables (ie site_landingpage) don't need an extra id column (although shouldn't break anything), simply have site_id & landingpage_id as a joint primarykey.
My issue is, I'm having trouble grasping DiscriminatorColumn and DiscriminatorMap in Doctrine's Class Inheritance.
I have a products entity that is considered the parent class / table.
There are several child entities that inherit the product entity. (models, parts, and options)
I feel like I should be able to use the primary key to link both tables... But how do I do that with DiscriminatorColumn?
Here is the general idea of what I want to happen...
Fetch all model objects from database while inheriting product parent entity
SELECT object
FROM parts_object parts
LEFT JOIN products_object po
ON parts.product_fk = po.product_id
Or... Fetch all part objects from database while inheriting product parent entity
SELECT object
FROM parts_object parts
LEFT JOIN products_object po
ON parts.product_fk = po.product_id
Ideally I want this done using Doctrine instead of some custom SQL.
Do I need to setup a "type" column for the parent table so each row defines whether it's a part, model, or option?
Doctrine inheritance docs
Okay, I'll try to explain this as simple as possible.
Let's start with DiscriminatorColumn
Discriminator column is basically, as it says, a column in your database. Its used to store, a key, if you like which helps to identify what kind of object you're currently querying, based on your DiscriminatorMap configuration.
DiscriminatorMap is the way you map each of those keys to an entity. You said you have the following
Product [parent]
Model [child of parent]
Part [child of parent]
Option [child of parent]
Then, your discriminator map should look something like this, for example:
#DiscriminatorMap({
"model" = "AppBundle\Entity\Model",
"Part" = "AppBundle\Entity\Part",
"Option" = "AppBundle\Entity\Option"
})
Always pay attention to your last definition in your mapping. The last line must end without a comma!
As of InheritanceType I would suggest you to use #InheritanceType("JOINED") because this will let you have single table for each of your child classes.
Every child class must extend your Product entity class, which is obviously the parent. Each child class must not define $id property, because of the inheritance mapping.
Then querying for records by specific type comes with the following query:
"SELECT product FROM AppBundle\Entity\Product product WHERE product INSTANCE OF AppBundle\Entity\Part"
The query will search only for records mapped to this entity only.
If you have any questions, don't hesitate to ask.
Edit as of new comment
-----------------------
A little bit more explanation. You do not need to create any extra property/column in your entity mappings. The moment you add this annotation #DiscriminatorColumn(name="discr", type="string") doctrine will create that column automatically for you. The column from this example would be named discr with type of VARCHAR.
I still don't understand what is used to join the tables. How does doctrine know to link the ids between the product and model
About this part. If you use #InheritanceType("JOINED") this would mean that your GeneratedValue ID would be set in your main entity - Product. Then each of the child entities that extend Product would automatically get the same ID, which is why you don't need to specify $id property in your child entities.
Lastly, how can you check which entity type you're currently viewing for example. Consider the following scenario, each of your child entities extends Product and we will perform a dummy search for a record:
$product = $entityManager->find('AppBundle:Product', 1); // example
Now, if you actually go and do a var_dump($product) you will notice something interesting. The object would be an instance of either Model,Part or Option because each of these entities are defined in your discriminator map and Doctrine automatically maps your records based on that.
Later, this can come handy in situations like this:
if( $product instanceof \AppBundle\Entity\Part ) {
// do something only if that record belongs to part.
}
If you want to use DiscriminatorMap for Doctrine, so you should use Doctrine, but not SQL.
Basic setup is:
/**
* #ORM\Table(name="product")
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\ProductRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="productType", type="string")
* #ORM\DiscriminatorMap({
* "Product" = "Product",
* "Model" = "Model",
* "Part" = "Part",
* "Option" = "Option",
* })
*/
class Product
{
...
}
MyApp\ProductBundle\Entity\Model
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\ModelRepository")
*/
class Model extends Product
{
}
MyApp\ProductBundle\Entity\Part
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\PartRepository")
*/
class Part extends Product
{
}
MyApp\ProductBundle\Entity\Option
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\OptionRepository")
*/
class Option extends Product
{
}
Then if you need to get all products at controller
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository("MyAppProductBundle:Product");
$products = $repo->findAll();
Then if you need select all models, just setup proper repository
$repo = $em->getRepository("MyAppProductBundle:Model");
$models = $repo->findAll();
In my Symfony2 app I have four entities that together make a nav menu:
{ Category, Level1Item, Level2Item, Level3Item }
Through "parent" en "children" properties they are connected and a hierarchy is formed.
In the MySql-db, the tables have a field called parent_id (except for the Categories as they are on the root level).
In addition, there is a property "order" (mapped to the db-field "order_id").
I feed the categories via the entity manager to a Twig template.
The template iterates over the items in level1, 2 and 3 if existent.
The order in which all items are displayed is in line with the item id's.
However, I would like to make the order-property leading.
In most cases, the order would come down do: { parent_id, order_id }.
Because the child-items for each parent could have the order 1, 2, 3, the order_id's itself are not unique. They are though within each parent-pool, no two children with order_id 2 for parent_id 1 for example.
I know how to force the order using sorting-functionality when using the QueryBuilder, or when using a custom function in an entityRepository. But how to set a default result order for the ->findAll() method that Twig will work with, drilling down to the children...?
You can specify order in entity mapping annotation like this:
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
* #ORM\OrderBy({"name" = "ASC"})
*/
private $children;
I am having problems generating a app(using "seam generate" after "seam create-project")
With identifying relationship like Role , User and User_has_Role.
It generates the 3 one-to-many , many-to-one entities instead of 2 #manytoMany entities.
I have seen a similar question here seam-gen doesn't generate EntityQuery interfaces for #ManyToMany members
In this post here, he managed to generate many to many entities http://community.jboss.org/thread/146500
Can seamgen generate many-to-many entities ?
If i use hibernate tools separately it generates the entities correctly.
I used seamgen 2.2.1CR2
Ok, so this is not possible to do with seam-gen as the question you are linking too.
This is how to do it manually (which is very easy).
In your User.java entity, write the following.
#UserRoles
#ManyToMany
#JoinTable(name = "UserRoles", joinColumns = #JoinColumn(name = "userId"), inverseJoinColumns = #JoinColumn(name = "roleId"))
public List<Role> getRoles() {
return roles;
}
This should be enough to generate/map the UserRoles table for you