I created a BaseBundle with a SuperUser MappedSuperClass who extends FOSUserBundle User
use FOS\UserBundle\Model\User as FOSUser;
/*
* #ORM\MappedSuperclass
*/
class SuperUser extends FOSUser
{
/**
* #var string
* #ORM\Column(name="locale", type="string", nullable=true)
*/
protected $locale;
}
In my Project I installed my BaseBundle. I created a User entity who extends my SuperUser class.
/*
* User class
*/
class User extends SuperUser
{
/**
* #var string
*/
private $fullName;
}
The problem is that when I create the table, Doctrine see only FOSUser properties and User properties but not my SuperUser properties. I can see only fullName and FOSUser properties.
My SuperUser class is bypassed..
I noticed that User class is with YAML notation, SuperUser is with PHP Annotation while FOSUser Class is with XML notation. I don't know if this create problems.
From the documentation:
A bundle can accept only one metadata definition format. For example, it's not possible to mix YAML metadata definitions with annotated PHP entity class definitions.
Try to add the YML mapping of your MappedSuperClass
Related
I have some problem using Doctrine and inheritance in Symfony 5.
First, I have a base entity with only 2 dates (createdAt and updatedAt).
/**
* #ORM\MappedSuperclass()
*/
class BaseEntity
{
/**
* #ORM\Column(type="datetime")
*/
protected $createdAt;
/**
* #ORM\Column(type="datetime")
*/
protected $updatedAt;
}
Next, I have another class extending the BaseEntity (for example "MyClass1").
/**
* #ORM\Entity(repositoryClass=MyClass1Repository::class)
* #ORM\MappedSuperclass()
*/
class MyClass1 extends BaseEntity
{
/* Some properties */
}
Finally, I have my class extending MyClass1 (for example "MyClass2").
/**
* #ORM\Entity(repositoryClass=MyClass2Repository::class)
*/
class MyClass2 extends BaseEntity
{
/* Some properties */
}
When I migrate my database (make:migration), Doctrine creates 2 tables :
myclass1
myclass2
Can you tell me where I am wrong ? I'd have only 1 table : myclass2.
The mistake is in the MyClass1 definition. From the Doctrine documentation about inheritance mapping.
A mapped superclass is an abstract or concrete class that provides persistent entity state and mapping information for its subclasses, but which is not itself an entity. Typically, the purpose of such a mapped superclass is to define state and mapping information that is common to multiple entity classes.
A Mapped superclass itself is not an entity, by adding the #Entity next to the #MappedSuperclass you're effectively making it an entity. The mapped superclass should not be an entity and is not query-able.
We encounter an issue with inheritance on Symfony 5.
We created a UserBundle bundle which includes a simple User entity (id, email, password): the purpose of this bundle is to be able to easily reimport it into our various projects.
In some projets, we want to extend this entity to add some specific fields (phone, address, ...), but that's not always the case.
Here is the code we had set up:
UserBundle > User class:
<?php
namespace MyCompany\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\MappedSuperclass()
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
...
User entity inherited:
<?php
namespace App\Entity;
use MyCompany\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #UniqueEntity(fields={"email"})
*/
class User extends BaseUser
{
/**
* #ORM\Column(type="string", length=50)
*/
private $phone;
...
The inheritance works fine: the problem comes from projects for which the User entity of the bundle is sufficient for us.
=> The User class of the bundle is defined as Mapped superclass in order to be able to extend it but Doctrine can't create an associated table if there is no class which inherits from it...
We could systematically create an empty User class in src/ which inherits from the User class of the bundle, but that doesn't seem super clean to me...
I went on this post but that doesn't seem to work: Doctrine Inheritance replacement
Could anyone help us on this problem? I imagine that a solution must exist...
Thank you in advance!
The idea below does not work.
As #Cerad already pointed out, the best approach probably really is to define the concrete User in each app. Thats also what FOSUserBundle does.
I'd go with the following approach: Declare the mapped superclass abstract.
/**
* #ORM\MappedSuperclass()
*/
abstract class AbstractBaseUser implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
...
And implement a concrete empty child class User inside your bundle.
/**
* #ORM\Entity
*/
class User extends AbstractBaseUser
{
}
That way each app can either use the User of your library or inherit from it.
I've got a User Entity defined (mapping in yml)
namespace My\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class User
{
...
And I created a child class that inherits from that entity, so that I can add some custom validation methods and a few fields that I need but do not need to be persisted (e.g. ConfirmPassword, ConfirmEmail fields)
namespace My\SecondBundle\EditModels;
use My\CoreBundle\Entity\User;
class UserModel extends User
{
When the user submit a registration form, I map the request to a UserModel entity, and if it is valid I try to persist the user.
The following code throws an exception
$entityManager->persist($userModel);
//=>The class 'My\SecondBundle\EditModels\UserModel' was not found in the chain configured namespaces My\CoreBundle\Entity
Question: How can I persist $userModel (instance of UserModel) as a User entity class? Possible options:
Do not use an inherited class and add custom fields and validation method to the User entity itself
Copy the fields from the UserModel to the User entity and persist the user entity
I don't think I should use Doctrine inheritance mechanism as I do not want to save the extra fields.
Thank you
I think your problem here, is that you've just configured My\CoreBundle\Entity namespace in Doctrine2, but the entity you actually want to persist is located in My\SecondBundle\EditModels.
Usually when inheriting classes marked as #ORM\Entity() the class you are extending from must have the class annotation #ORM\MappedSuperclass(). But normally you use this for single table inhertiance e.g., not for your usecase.
In my opinion the approach to split database related attributes from the others, is not affordable. I would keep validation related stuff in the model itself - you need it in your create/update action.
I'm not familiar with XML configuration, but when using annotations you need to mark each property to be mapped with database (using #ORM\Column()). So Doctrine will ignore all the other attributes and methods entirely.
So here I share my recently developed AbstractModel for you, to see how I've implemented validation (with respect/validation):
<?php
namespace Vendor\Package\Model;
use Doctrine\ORM\Mapping as ORM;
/**
* Abstract Model
*
* #ORM\MappedSuperclass()
*/
abstract class AbstractModel
{
/**
* #var \Respect\Validation\Validator
*/
protected $validator;
/**
* AbstractModel constructor
*/
public function __construct()
{
$this->validator = static::validation();
}
/**
* Defines validation for this model
*
* #return \Respect\Validation\Validator
*/
public static function validation() : \Respect\Validation\Validator
{
return \Respect\Validation\Validator::create();
}
/**
* Executes validations, defined in validation method.
*
* #return bool
*/
public function isValid() : bool
{
if (is_null($this->validator)) {
$this->validator = new \Respect\Validation\Validator();
$this->validation();
}
return $this->validator->validate($this);
}
}
A model which extends from the AbstractModel needs to implement a static validate method, to define class validation:
<?php
namespace Vendor\Package\Model;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
* #ORM\Table(name="my_model")
*/
class MyModel extends AbstractModel
{
/**
* #var string
* #ORM\Column(type="string")
*/
private $name;
/**
* Defines validation for this model
*
* #return \Respect\Validation\Validator
*/
public static function validation() : \Respect\Validation\Validator
{
return \Respect\Validation\Validator::create()
->attribute('name', \Respect\Validation\Validator::notEmpty()->stringType()->length(null, 32))
;
}
// getter, setter, ...
}
Each entity, persisted to database, will have the $validator property and all these methods, but because I left annotations here (and pretty sure this also works with xml/yaml) Doctrine ignores it.
And this way you also keep validation related stuff out of the model class itself, which is good for readability. The validation itself should be defined in the model itself, imho. But this respect/validation framework is neat way to achive this. Hope this helps :)
Currently I use the storage agnostic pattern in my project. This pattern is pretty simple: I've my Model\Model and my specific storage layer that extends of Model\Model (like Entity\Model and Document\Model).
The problem I'm facing is with polymorphic objects. If I don't extend an entity Doctrine does not recognize the inheritance mapping. In that way I need to replicate my code for every storage layer:
class Model\Option {}
class Model\OptionNumber extends Option {}
class Entity\Model extends Model\Option {}
I would like to have:
class Entity\OptionNumber extends Model\OptionNumber{}
But it's not possible. I've to code in Entity\OptionNumber once PHP have no support for multiple inheritance.
Is there any pattern that solve this problem?
Just for reference, I'm using Doctrine 2.2 and Symfony 2.3.
Sure!
Model:
abstract class Option implements OptionInterface
{
/**
* #var int
*/
protected $id;
/**
* #var string
*/
protected $name;
/**
* #var string
*/
protected $presentation;
/**
* #var Collection of OptionChoiceInterface
*/
protected $choices;
}
Entity:
class Option extends BaseOption
{
}
Document:
class Option extends BaseOption
{
}
I have abstract entity (App - base core):
namespace App\Bundles\AppBundle\Entity;
abstract class App extends ContainerAware implements AppInterface
{
// .....
}
and self entity:
namespace AppRus\Bundles\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM,
App\Bundles\AppBundle\Entity\App as BaseApp;
/**
* App
*
* #ORM\Table(name="apps")
* #ORM\Entity(repositoryClass="AppRus\Bundles\AppBundle\Entity\AppRepository")
*/
class App extends BaseApp
{
// ....
}
and entity for control revisions:
namespace App\Bundles\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* AppHistory
*
* #ORM\Table(name="apps_history")
* #ORM\Entity(repositoryClass="App\Bundles\AppBundle\Entity\AppHistoryRepository")
*/
class AppHistory
{
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="App\Bundles\AppBundle\Entity\App")
* #ORM\JoinColumn(name="app_apple_id", referencedColumnName="apple_id")
*/
private $app;
}
I can't create relation AppHistory#app to App#apple_id
When I set entity to abstract class App, then doctrine create a new table "App"
When I set MappedSuperClass to abstract class App, I have error: http://docs.doctrine-project.org/en/latest/reference/inheritance-mapping.html#mapped-superclasses
How can I create relation from AppHistory to abstract core App?
My understanding is that that's not possible. I think you're doing things the wrong way, at least for Doctrine2.
First of all, from the Doctrine2 docs you mention:
A mapped superclass is an abstract or concrete class that provides persistent entity state and mapping information for its subclasses, but which is not itself an entity.
You can't create a relation to something that's not an entity!
To solve your immediate problem (~version control~ in doctrine), check out EntityAudit on Github instead.