Overwriting entities from another bundle in Symfony2 - php

We're writing a custom CMS based on Symfony2 and are currently working on the user system. Our bundle has defined a BaseSiteUser that contains all the most common properties that a site may need for it's user data, and a SiteUser that extends BaseSiteUser and defines a table for the entity.
The idea was that for each individual project we'd overwrite the SiteUser entity by creating a more specific one in the new project's namespace, or use the default one if we don't specify a new one.
Our bundle architecture looks somewhat like this:
OurCompany
CMSBundle
Entities
BaseSiteUser.php
SiteUser.php
CustomerName
CustomerProjectBundle
CMSBundle
Entities
SiteUser.php
Our problem is that we can't build the model so that the SiteUser defined in CustomerName\CMSBundle is used instead of the one in OurCompany\CMSBundle. Instead, we get this error:
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'siteuser' already exists.
We tried consulting the documentation, but that part has not been covered yet. Searching the web did not provide a solution, either.
We are using the annotation method for defining the model.

I have deleted my previous answer as it as wrong :). Just checked implementation of FOSUserBundle. They are using inheritance mapping. So in your core CMSBundle define BaseSiteUser as #MappedSuperclass and in your overridden bundle just extend BaseSiteUser.

I think this solution should work (pseudocode):
OurCompany
CMSBundle
Entities
/**
* BaseSiteUser
*
* #ORM\Entity(repositoryClass="OurCompany\CMSBundle\Repository\SiteUserRepository")
* #ORM\Table(name="siteuser")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="discr", type="string")
*
* DON'T USE EXPLICIT DISCRIMINATOR MAP (it will be generated automatically) !!!
* # ORM\DiscriminatorMap({"basesiteuser" = "BaseSiteUser", "siteuser" = "SiteUser"})
*/
BaseSiteUser.php
/**
* SiteUser
*
* #ORM\Table(name="siteuser")
*/
SiteUser.php => class SiteUser extends CustomerName\CMSBundle\Entities\SiteUserCustom
CustomerName
CustomerProjectBundle
CMSBundle
Entities
/**
* SiteUserCustom
*
* #ORM\Table(name="siteuser")
*/
SiteUserCustom.php => class SiteUserCustom extends OurCompany\CMSBundle\Entities\BaseSiteUser
Don't forget to register your bundles in AppKernel.php.
If you want to be able to define new fields in SiteUserCustom add your CustomerNameCMSbundle to the entity-manager configuration:
orm:
entity_managers:
default:
mappings:
OurCompanyCMSbundle: ~
CustomerNameCMSbundle: ~

Related

Symfony 4 table relationships do not work out of naming strategy?

I have created two entities of existing database tables, these tables use the doctrine conventions for table relationships, I need to relate the tables to be able to work, the entities work by consulting data, but not between them.
Table name "Articulos"
class Articulos
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
*/
private $ID_Articulo;
/**
* #ORM\Column(name="ID_Clasificacion_Articulo", type="integer")
* #ORM\ManyToOne(targetEntity="ClasificacionesArticulos")
*/
private $ID_Clasificacion_Articulo;
.......
Table name "ClasificacionesArticulos"
class ClasificacionesArticulos
{
/**
* #ORM\Column(name="ID_Clasificacion_Articulo", type="integer")
* #ORM\Id()
* #ORM\OneToMany(targetEntity="Articulos", mappedBy="ID_Clasificacion_Articulo")
*/
private $ID_Clasificacion_Articulo;
/**
* #ORM\Column(type="string", length=150)
*/
private $Codigo;
/**
* #ORM\Column(type="string", length=150)
*/
private $Nombre;
.........
When I consult any of the entities, returns result without children of relationships. I suppose it's because of the names of the fields id does not use the name strategies, but I can not change them in the database, I have to adapt to it by requirements.
If someone has an idea, I thank you very much
This can be accomplished by implementing custom Doctrine naming strategy. In Symfony entity, use camelCase to avoid any problems with naming. But, if you need specific table names follow the guide
You have to implement NamingStrategy class:
class CustomNamingStrategy implements NamingStrategy
{
}
register it as a service by adding following to the end of the the config/services.yaml :
app.naming_strategy.custom:
class: App\Service\CustomNamingStrategy
autowire: true
Then specify naming strategy by editing config/packages/doctrine.yaml as follows:
naming_strategy: app.naming_strategy.custom
I believe you are looking for propertyToColumnName method, as Doctrine says
If you have database naming standards, like all table names should be
prefixed by the application prefix, all column names should be lower
case, you can easily achieve such standards by implementing a naming
strategy.

How to extend repositories in Doctrine 2? for share common functionalities in different table names?

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.

Symfony Annotations are not parsed

I have installed the FOSUserBundle and installed it as per its detailed installation guide (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md).
When i run php console doctrine:schema:update --force for the first time, it populates the users table with all of the default fields that the FOSUserBundle has defined.
Unfortunately it appears to be completely missing the fields which i have added to my user entity and i am wondering if its utilising the configuration file which is specified in the installation guide instead of using the annotations which are in the entity.
It also appears to be ignoring the other entities within the same Bundle.
namespace Acme\UserBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User extends BaseUser {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="Bureau", mappedBy="id")
*/
protected $bureau;
public function __construct() {
parent::__construct();
}
}
This is my user entity, of which bureau is being completely ignored regardless if it has a relationship or not.
Edit
As per requested, please find below the orm config file. It's the default file as per the configuration.
I have suspected this to be the problem, but i wasnt sure if annotations and the config file could work together.
Acme\UserBundle\Entity\User:
type: entity
table: users
id:
id:
type: integer
generator:
strategy: AUTO
Edit 2
I have found that if i remove the orm configuration file that it all magically works again!!
So i would adjust my question for clarity.
Updated question
If an orm configuration file exists, are annotations ignored?
When you generate entities with the console, you are asked on the format, which is:
xml
yaml
annotations
Regardless of what you choose, there are no signifiers telling Doctrine which to use besides the fact one exists. In order, YAML takes priority over annotations, and so it should.

FosUserBundle don't persist data in mongodb database

I use symfony2 and the mongoDb ODM. Today I have installed FosUserBundle.
My User class is like that :
use FOS\UserBundle\Document\User as BaseUser;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class User extends BaseUser
{
/**
* #MongoDB\Id(strategy="auto")
*/
protected $id;
public function __construct()
{
parent::__construct();
}
}
My problem is after created a User with FosUserBundle create command, only the id of user is persisted in mongodb document.
If I add the following in my User class :
/**
* #MongoDB\String
*/
protected $username;
The create command persist Id and the good username.
Of course, it's the same with all the initial fields of FOS\UserBundle\Document\User (BaseUser).
It looks like the inheritance mapping is not working properly.
Check that your Doctrine configuration in config.yml is set to:
auto_mapping: true
To work around this another way you would need to add the complete mapping information to your User entity extending the one from the FOSUserBundle.
With Doctrine\ORM it is normally the #ORM\MappedSuperClass annotation which provides the mapping for the extending class. In FOSUserBundle's mongodb xml mapping it is this line:
...
<mapped-superclass name="FOS\UserBundle\Document\User" collection="fos_user_user">
...
Solution 1:
Try this:
copy the mapping xml from FOSUserBundle over to your UserBundle into Resources/config/doctrine/User.mongogb.xml
change it to fit your own Entity Class
remove mapped-superclass node
add the id field as Id with auto strategy
You can then ommit the #MongoDB annotations on your entity completely.
To save somebody's time.
I also came across the issue of non-working mapping for ODM User entity and could not understand why it doesn't work. The reason was I must have extended my user entity from FOS\UserBundle\Document\User and not from FOS\UserBundle\Model\User

Polymorphic relationships with Doctrine2

How do I create traditional polymorphic relationships with Doctrine 2?
I have read a lot of answers that suggest using Single Table Inheritance but I can't see how this would help in my situation. Here's what I'm trying to do:
I have some utility entities, like an Address, an Email and a PhoneNumber.
I have some 'contactable' entities, like a Customer, Employer, Business. Each of these should contain a OneToMany relationship with the above utility entities.
Ideally, I'd like to create an abstract base class called 'ContactableEntity' that contains these relationships, but I know it is not possible to put OneToMany relationships in mapped superclasses with doctrine-- that's fine.
However, I am still at a loss at how I can relate these without massive redundancy in code. Do I make Address an STI type, with a 'CustomerAddress' subclass that contains the relationship directly to a Customer? Is there no way to reduce the amount of repetition?
Why not just make your base ContactableEntity concrete?
EDIT:
Just did a few experiments in a project I've done that uses CTI. I don't see any reason that the same strategy wouldn't work with STI.
Basically, I have something like:
/**
* Base class for orders. Actual orders are some subclass of order.
*
* #Entity
* #Table(name="OOrder")
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"CAOrder" = "CAOrder", "AmazonOrder" = "AmazonOrder"})
*/
abstract class Order {
/**
* CSRs can add notes to orders of any type
* #OneToMany(targetEntity = "OrderNote", mappedBy = "order", cascade={"all"})
* #OrderBy({"created" = "ASC"})
*/
protected $notes;
// ...
}
/**
* #Entity
*/
class AmazonOrder extends Order {
/**
* #Column(type="string", length="20")
*/
protected $amazonOrderId;
// ...
}
/**
* #Entity
*/
class OrderNote {
// ...
/**
* #ManyToOne(targetEntity="Order", inversedBy="notes")
*/
protected $order;
// ...
}
And it seems to work exactly as expected. I can get an OrderNote, and it's $order property will contain some subclass of Order.
Is there some restriction on using STI that makes this not possible for you? If so, I'd suggest moving to CTI. But I can't imagine why this wouldn't work with STI.
If the contactable entity shall be abstract (#MappedSuperclass) you'll need to use the ResolveTargetEntityListener provided by Doctrine 2.2+.
It basically allows you to define a relationship by specifying an interface instead of a concrete entity. (Maybe you want to define/inherit several interfaces as you speak of multiple "contactables"). For instance you then can implement the interface in your abstract class or concrete class. Finally you'll need to define/associate the concrete class (entity) to the related interface within the config.yml
An example can be found in the Symfony docs: http://symfony.com/doc/current/cookbook/doctrine/resolve_target_entity.html

Categories