I have two entity types: \Model\News and \Model\News\Category.
\Model\News: (without couple of fields)
namespace Model;
/**
* #Entity
* #Table(name="news")
*/
class News extends \Framework\Model {
/**
* #Id
* #GeneratedValue
* #Column(type="integer")
*/
protected $id;
/**
* #ManyToOne(targetEntity="\Model\User", inversedBy="news")
*/
protected $author;
/**
* #ManyToOne(targetEntity="\Model\News\Category", inversedBy="news")
*/
protected $category;
}
\Model\News\Category:
namespace Model\News;
/**
* #Entity
* #Table(name="news_category")
*/
class Category extends \Framework\Model {
/**
* #Id
* #GeneratedValue
* #Column(type="integer")
*/
protected $id;
/**
* #Column(type="string", length=50, unique=TRUE)
*/
protected $name;
/**
* #OneToMany(targetEntity="\Model\News", mappedBy="category")
*/
protected $news;
/**
* Constructor
*/
public function __construct() {
parent::__construct();
$this->news = new \Doctrine\Common\Collections\ArrayCollection;
}
}
Table data from \Model\News:
id | category_id | author_id
------------------------------- ...
4 | 1 | NULL
Table data from \Model\News\Category:
id | name
---------------
1 | General
---------------
2 | Other
While I'm loading News type Entity with this particular code and doing dump with \Kint class:
$sId = '4';
$sModel = 'Model\News';
$oObject = $entity_manager->find($sModel, $sId);
d($oObject);
It returns me this:
My question is, why category property from $oObject variable has NULL values despite the fact that the category with id = 1 exists in database?
UPDATE:
After above code, I want to load this category (with ID=1) separately. Same thing. But... when I'm loading a category with other ID (for example, 2) it's loading with no problems:
$oObject2 = $entity_manager->('\Model\News\Category', '2');
I have no idea what to do now...
If you know that the category entity will be accessed almost each time you load news, you might want to eager load it (force doctrine to load it when News is loaded instead of when a Category property is called).
In order to do so, just add the fetch="EAGER" annotation on your association
/**
* #ManyToOne(targetEntity="\Model\News\Category", inversedBy="news", fetch="EAGER")
*/
protected $category;
So... I finally came to solve the problem thanks to #asm89 from #doctrine IRC chat in freenode server.
Doctrine is creating a proxy class which uses "lazy loading". Data is loaded only when one of getters is used.
So, after using $oObject->getCategory()->getName(), my category object available from $oObject->getCategory() was filled up with proper data.
Related
Can the discriminator mapping strings of a Doctrine entity be fetched from a database foreign table field? If not, how to handle inheritance type mapping in such a situation?
Considering a Doctrine 2.7.2 abstract entity Person with concrete entities A and B like:
/**
* #MappedSuperclass
* #Table(name="PERSON")
*/
abstract class Person {
/**
* #Id
* #GeneratedValue
*/
protected $id;
/**
* #Column(name="name",type="string")
*/
protected $name;
/**
* #OneToOne(targetEntity="PersonType")
* #JoinColumn(name="type", referencedColumnName="id")
*/
protected $type;
}
/**
* #Entity
* #Table(name="PERSON")
*/
class A extends Person {}
/**
* #Entity
* #Table(name="PERSON")
*/
class B extends Person {}
A Person has a type bound to a PersonType entity like:
/**
* #Entity
* #Table(name="PERSON_TYPE")
*/
class PersonType {
/**
* #Id
* #GeneratedValue
*/
protected $id;
/**
* #Column(name="code",type="string")
*/
protected $code;
/**
* #Column(name="name",type="string")
*/
protected $name;
}
How to get the right class instantiated by repository query methods like find(), findBy(), findById(), etc?
Database set:
SELECT * FROM PERSON:
id name type
1 John 1
2 Tom 2
SELECT * FROM PERSON_TYPE:
id code name
1 A Type A
2 B Type B
Expected results:
$entityManager->getRepository("A")->findOneById(2); // null
$entityManager->getRepository("B")->findOneById(2); // B object (Tom)
$entityManager->getRepository("A")->findOneById(1); // A object (John)
I can't find how to specify this using a discriminator.
Alternatively I have a working solution implementing a SQLFilter with the inconvenience to have to enable or disable filter.
An alternative solution may be to populate $discr?
(I can of course use the Person::findByType() method but it would be nice to have this feature directly managed by the respective repositories).
I have a bit of an issue. i am modeling a DB structure using Doctrine 2 entities and I got stuck with one relation.
What I need to represent in the database is a matrix like table:
| |A|B|C|D|
|A| x
|B|x x
|C| x x
|D| x
What the table shows is what offers are available with other offers (x=true, empty = false).
What I tried to do is a Many to Many relationship with doctrine, hoping that this will generate a mapping table, but Doctrine does not do that.
Here is my entity:
/**
* #ORM\Entity #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="Core\Repository\Main")
* #ORM\Table(name="offer_availability")
*/
class OfferAvailability implements JsonSerializable
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var String
* #ORM\Column(type="string")
*/
protected $name;
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\ManyToMany(targetEntity="Core\Entity\Offers\OfferAvailability", mappedBy="availableWith", inversedBy="availableWith", fetch="EXTRA_LAZY")
*/
protected $availableWith;
I know I can create the mapping table entity manually and Use One to Many relations. But what I am wondering if there is a more clever way of approaching this.
Never mind, i have found the issue. Creating mappings in this case only confuses Doctrine. Removing mapping and inversion compleely solved the problem:
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\ManyToMany(targetEntity="Core\Entity\Offers\OfferAvailability", fetch="EXTRA_LAZY")
*/
protected $availableWith;
This generated a mapping table:
------------------------------------
|offeravailablity_offeravailability|
------------------------------------
|offeravailability_source |
|offeravailability_target |
------------------------------------
This question is about Symfony2 table relationships using ORM. I have three tables/entities that are related to each other. The relationship is very similar to Wordpress Posts, Categories and Categories relationship tables.
Table 1 contains posts.
Table 2 contains categories
Table 3 contains relationships between the categories and posts.
I want to be able to have the categories property in the posts table and a posts property in the categories table. So that when I call.
Categories->posts : I should get posts in that category.
Posts->categories : I should get the categories the post belongs to.
I want to have unique categories per table and I want all posts to point to a category without having to create a new entry for the category that already exists which is what ManyToOne or OneToMany is offering this is why the third table I think is necessary.
For example here is the relationships
class Category_relationship
{
/**
* #var integer
*
* #ORM\Column(name="object_id", type="bigint")
*
* #ORM\ManyToOne(targetEntity="Worksheet", inversedBy="category_relationships")
* #ORM\JoinColumn(name="worksheet_id", referencedColumnName="id", nullable=FALSE)
*/
private $objectId;
/**
* #var integer
*
* #ORM\Column(name="category_id", type="bigint")
*
* #ORM\ManyToOne(targetEntity="Category", inversedBy="categories")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=FALSE)
*/
private $categoryId;
}
Here is the Category class:
class Category
{
/**
* #ORM\OneToMany(targetEntity="Category_relationship", mappedBy="categoryId", cascade={"persist", "remove"}, orphanRemoval=TRUE)
*/
protected $posts;
}
Here is the Category class:
class Posts
{ /**
* #ORM\OneToMany(targetEntity="Category_relationship", mappedBy="objectId", cascade={"persist", "remove"}, orphanRemoval=TRUE)
*/
protected $categories;
}
I want to create a system where I can assign posts to a category but the category table can only contain 1 entry about the category. I also want to be able to use expressions link;
Post->categories
Category->posts
or
Post->AddCategory()
Category->AddPost()
Thanks for your help.
It seems that you want a simple many-to-many relationship.
Every post can have multiple categories, and every category have list of related posts. Many to many handles pivot table by itself.
So, in Post entity you have to declare relationship that way:
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="posts")
* #ORM\JoinTable(name="PostsCategories",
* joinColumns={#ORM\JoinColumn(name="post_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="category_id", referencedColumnName="id")}
* )
**/
protected $categories;
Remember about using Doctrine\ORM\Mapping with ORM alias (you don't have to import all subclasses separately):
use Doctrine\ORM\Mapping as ORM;
After that, you need to create a new ArrayCollection in class constructor:
public function __construct()
{
$this->categories = new ArrayCollection();
}
And add proper methods, like addCategory:
public function addCategory(Category $category)
{
$this->categories[] = $category;
return $this;
}
You can also add them automatically with:
php app/console doctrine:generate:entities BundleName:EntityName
Same thing in Category entity, but with a little different definiton:
/**
* #ORM\ManyToMany(targetEntity="Post", mappedBy="categories")
**/
protected $posts;
You can find all of these information in Doctrine docs
I'm new to Symfony and Doctrine and I'm trying to join two tables so I can then access the associated values from Twig template easily.
Here is my db scheme:
+--------------------------------------+--------------------+
|Messages | User |
|id user text user_id | id name |
|1 testuser something 1 | 1 John |
+--------------------------------------+--------------------+
This is my Message entity:
/**
* #ORM\Entity
* #ORM\Table(name="Messages")
*/
class Message {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
protected $user_id;
/**
* #ORM\Column(name="text", type="text")
*/
protected $text;
/**
* #ORM\ManyToOne(targetEntity="User")
* */
private $user;
}
And this is my User entity:
/**
* #ORM\Entity
* #ORM\Table(name="Users")
*/
class User {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="name", type="string", length=255)
*/
protected $name;
}
Then in controller I send $messages variable to Twig template:
$messages = $this->getDoctrine()->getEntityManager()->getRepository('MeMyBundle:Message')->findAll()
And the question is: Is the joing made properly? How can I access name property through message in Twig? Thanks.
Because of your many to one relationship, the variable $user in the Message class should be an object of type User. Because your variables $user and $name are private or protected, you should make getters and setter for them or make Doctrine generate them for you. After that $messages[i]->getUser()-getName() should work. (Generating getters and setters)
For more info about accessing attributes in relationships, take a deeper look at Fetching Related Objects section of the documentation.
From the same symfony documentation page "Of course, if you know up front that you'll need to access both objects, you can avoid the second query by issuing a join in the original query."
If you want a true JOIN instead of a lazily loaded query you can write your own sql query following the documentation.
I have the following hierarchy of classes:
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"root" = "RootCategory", "sub" = "SubCategory"})
*/
class Category
{
/**
* #Id #Column(type="integer") #GeneratedValue
*/
protected $id;
}
/**
* #Entity
*/
class RootCategory extends Category
{
}
/**
* #Entity
*/
class SubCategory extends Category
{
/**
* #ManyToOne(targetEntity="RootCategory")
* #JoinColumn(name="parentId", referencedColumnName="id")
*/
protected $parent;
}
In this domain model, we have only two possible levels in the hierarchy, hence the distinction as RootCategory or SubCategory.
I find it redundant to have to specify an explicit discriminator column, where we could just use a simple rule as the discriminator:
If parentId is null, then this is a RootCategory
If parentId is not null, then this is a SubCategory
Is it possible with Doctrine 2, to use the NULL status of a column as the discriminator?
No, it's impossible.
If you look at the source code of Doctrine 2 you will see that he transforms DiscriminatorMap into array, that can't have NULL key, only string or integer.
Can you use the following in DQL
$qb->where('{...} IS NULL');