I am learning Symfony 3 with Doctrine 2.
When I have OneToMany relationship in Doctrine entity, what exactly I have to put into mappedBy annotation?
Is it the table name of current entity?
Or is it the entity shortcut?
Or is it the actual class name?
Imagine this simple example:
<?php
namespace AppBundle\Entity;
/**
* #ORM\Entity
* #ORM\Table(name="blog_category")
*/
class Category
{
// ...
/**
* #ORM\OneToMany(targetEntity="Article", mappedBy="category")
*/
private $articles;
// ...
}
Why is the "category" correct value for mappedBy? Why isn't it "blog_category" or "Category" (uppercase "C")? Or "AppBundle:Category"?
Now I figured it out. It is the name of related's entity class variable :-)
To give a complement, almost everything you could do in doctrine (could surely be applied for any other ORM/ODM), in almost every contexts (QueryBuilder, findBy methods, ...) you'll use property names rather than column names.
The reason is quite simple, an ORM deals with objects and their properties, abstracting the real tables and their columns, no matter of the database engine or anything else.
Related
I have been doing some research on this topic but so far I couldn't find anything helpful for my scenario.
In a brief: I have two tables Quote (table name: quote) and QuoteArchive (table name: quote_archive). Both share exactly the same columns and types. As far as I have read this turn into a Doctrine MappedSuper Class ex: MappedSuperclassQuote.
After that Quote and QuoteArchive entities will extend from the MappedSuperclassQuote and both will share exactly the same structure.
Quote has a custom Repository with some functions. QuoteArchive needs exactly the same Repository functions as in Quote with the only difference being the table name and the PK.
I have two doubts in this scenario:
How to extend Doctrine entities when the PK (#Id) is different in the child classes?
How to extend or share the same repository between entities when the only change is the table name.
For get a better idea this is how my current entities looks like:
/**
* #ORM\Entity
* #ORM\Table(name="quote")
* #ORM\Entity(repositoryClass="QuoteBundle\Entity\Repository\QuoteRepository")
*/
class Quote
{
/**
* #ORM\Id
* #ORM\Column(type="integer",unique=true,nullable=false)
* #ORM\GeneratedValue
*/
private $quoteId;
// ...
}
/**
* #ORM\Entity
* #ORM\Table(name="quote_archive")
* #ORM\Entity(repositoryClass="QuoteBundle\Entity\Repository\QuoteArchiveRepository")
*/
class QuoteArchive
{
/**
* #ORM\Id
* #ORM\Column(type="integer",unique=true,nullable=false)
*/
private $archiveId;
// ...
}
Last but not least:
class QuoteRepository extends EntityRepository
{
public function getCurrentQuoteId(int $OrigQuoteId)
{
$em = $this->getEntityManager();
$qb = $em->createQueryBuilder();
return $qb->select('q')
->from('QuoteBundle:Quote')
->where('q.origQuoteId =:origQuoteId')
->setParameter('origQuoteId', $OrigQuoteId)
->andWhere('q.quoteType =:quoteType')
->setParameter('quoteType', 'current')
->getQuery()
->getResult();
}
}
What is the problem here? I need to repeat the same exact function in QuoteArchiveRepository and change the table from quote to quote_archive and it's exactly what I am trying to avoid if possible.
Can any give me some ideas? Code example would be great :)
References:
Can we extend entities in Doctrine?
Doctrine: extending entity class
Doctrine How to extend custom repository and call the extended repository from doctrine entity manager
I think you're mistaking doing a MappedSuperclassQuote entity.
You have to inherit the Archive from the Quote.
Example : you have your Quote entity
The definition should be something like :
/**
* #ORM\Table(name="app_quote")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="quote_type", fieldName="quoteType", type="string")
* #ORM\DiscriminatorMap({
* "quote":"YourBundle\Entity\Quote",
* "quote_archive":"YourBundle\Entity\QuoteArchive"
* })
* #Gedmo\Loggable
* #ORM\Entity(repositoryClass="YourBundle\Repository\QuoteRepository")
*/
Why a JOINED inheritance ? Cause you want two separate tables (what SINGLE_TABLE is not doing) and you don't have a really abstract class (cause Quote AND QuoteArchive means something for you)
After, your table QuoteArchive should extends the first one :
/**
* #ORM\Entity
* #ORM\Table(name="app_quote_archive")
*/
class QuoteArchive extends Quote
{
...
}
Your column quote_type in app_quote will help you to know if this is an archived quote or not.
It provides you all you want :
- QuoteArchive will have access to functions inside QuoteRepository
- Each table has separated ids
One thing could be annoying for you : if you want to set a quote has archived, it's not so easy to change an entity type for now in Doctrine. In that case, it's better for you to use single_table joining type. All the datas are stored in a same table in database, making type change easy but you keep two different entities.
In Symfony2, I just try recently to think in terms of traits, to create some sort of behaviors.
Let's say I have an address attribute in an entity. I externalized attributes, getters and setters related to this in an AddressableTrait.
But what if address become an entity? I started to try to define my OneToMany relation in my trait, as if it was in a regular entity :
use Doctrine\ORM\Mapping as ORM;
class AddressableTrait {
/**
* #var
* #ORM\OneToMany(targetEntity="XXXX\GlobalBundle\Entity\Address", inversedBy="What to put here" )
*/
protected $addresses;
/**
* #return ArrayCollection
*/
public function getAddresses()
{
return $this->addresses;
}
/**
* #param ArrayCollection $addresses
*/
public function setAddresses($addresses)
{
$this->addresses = $addresses;
}
}
What to put in the inversedBy? The purpose of the trait if precisely to embed all the behavior feature, so I think that at least using traditionnal annotation/YML/XML,it's not possible to achieve.
I digged a bit into it and found this very interesting link that seems to allow you to defines relation via events, but there is still logic to add to "finish" relations.
UPDATE :
Using the above link, I managed to created dynamic ManyToMany relation. the schema update works when creating, but if I comment the dynamic relation, a schema:update --dump-sql doesn't remove it. It seems to work add-only. Any clue to force the dynamic mapping to stick to the real relations addition/removal?
Thanks a lot for your answers !
Nicolas
I encountered a problem using traits in entities. For regular database values (scalar, DateTime) traits worked fine, but when I tried to define entity relations in traits the doctrine migrations bundle would convert the property to a varchar field.
The only way I could find to fix creating proper entity relation properties was by moving them out of the trait and into the entity class itself.
Maybe there are already answers regarding this topic but I cannot find any information. If there are answers already, just post any links.
My question is:
I work with Symfony 2.5 and doctrine 2
A common problem I have is that I have an entity that I want to persist to different tables in the database. For this I usually create a superclass and extend it in two concrete entities that I persist to the database.
Now I have two entities deriving from the same base class. Is it possible to convert from one to another. Of course I have to persist them to the database separately.
One example. I have two classes: "quote" and "order" which are basically the same and extend the superclass "project"
/**
* #ORM\MappedSuperclass
*/
abstract class project {
/**
* #ORM\Column(type="text", nullable=true)
*/
private $description;
/**
* #ORM\ManyToOne(targetEntity="Bundle\Customer")
*/
private $customer;
/**
* #ORM\ManyToOne(targetEntity="Bundle\CustomerContact")
*/
private $customercontact;
... (getters/setters etc.)
}
class quote extends project { ... }
class order extends project { ... }
Now once a quote is confirmed I want to "convert" it to an order and keep all the base data (stored in the "project" part). Also I want to keep the quote itself so I kind of want to create a new order from the quote instead of using the "project" directly and just set a status there.
Of course I could write a constructor or something but I am searching for a way to clone that part of an object.
Maybe there is an alternative way where the class is not extended but the "project"-part is used within the class itself. Not sure but it seems to me like a lot of people could have run into such a problem already and there might be a smart solution for this kind of situation.
I'm happy with any links to docs too if applicable.
I have an abstract parent class called Divers which is extended by few other classes.
So, I use inheritance mapping with D2 using Single Table Inheritance strategy.
namespace MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* ParentClass
*
* #ORM\Table(name="PARENTCLASS")
* #ORM\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="idtable", type="string")
* #ORM\DiscriminatorMap({
* "CHILD-CLASS1" = "ChildClassOne",
* "CHILD-CLASS2" = "ChildClassTwo",
* "CHILD-CLASS3" = "ChildClassThree",
* "CHILD-CLASS4" = "ChildClassFour"
* })
*/
abstract class ParentClass
{
...
}
What I want to achieve is to display the discriminator in the browser with a little description that explains what is it to the user.
I googled for a solution like putting the discriminator in a joined table but found nothing.
Do you have any advice to achieve my goal ?
Thanks by advance for your help.
The discriminator column has special meaning for Doctrine 2 and thus cannot be part of a relation.
But there is an easy work around. Just add another column and give it the same value that your discriminator column has. The value will never change so it's easy enough to do. You can then of course use your new column in the same way as any other column.
I know having two columns with the same value is not ideal from a database perspective. But from an object perspective, it's no big deal since the discriminator column is never exposed as a property. And it's just the way doctrine works. It wants that column all to itself.
You can achieve it using PHP, whitout adding another field in the db as long as you don't need the field in a SQL query.
Since the discriminator is an abstract class, just adding a public abstract method returning your hard-coded discriminator value would do the trick. Then you can use your entity in twig or a json serializer.
abstract class ParentClass {
public abstract function getDiscriminator(): string; // The discriminator type
}
class ChildClassOne extends ParentClass
{
public function getDiscriminator(): string
{
return 'CHILD-CLASS1';
}
}
If you need to fetch in SQL, use $qb->andWhere($qb->isInstanceOf(ChildClassOne::class)) since the method or discriminator attribute is not available in sql.
I have a document with a field marked for persistence as a Collection
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/** #ODM\Document */
class Item
{
/**
* #ODM\Collection
*/
protected $things;
}
By default, when Doctrine maps the values from Mongo it will set $things to be an instance of Doctrine\Common\Collections\ArrayCollection.
This violates my domain model however as my domain objects expect the $things property to be an instance of my own collection class ThingsCollection. This class does various validations on the list of things.
How can I tell Doctrine to use my ThingsCollection class instead? I imagine I will have to make the ThingsCollection class implement the Doctrine\Common\Collections\Collection interface but that is not a problem if I can just figure out how to tell Doctrine about the mapping in the first place.
The answer is that it is not possible.
Furthermore, despite the documentation being confusing and saying otherwise, for a general Collection property mapping as above (not a reference) an ArrayCollection is not returned. A basic php array is given.
So, it appears in this situation, you are forced to work with arrays, at least currently.