I have 2 Symfony bundles. AdminBundle will always be installed. PageBundle may or may not be installed.
I want to define a base Entity called AdminModule (name, controller class, description, enabled), and also a PageModule which simply inherits from AdminModule ( the entities controller will implement a specific interface).
<?php
namespace AdminBundle\Entity;
/**
* Admin Component
*
* #ORM\Entity
* #ORM\Table(name="admin_module")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"page" = "\PageBundle\Entity\PageComponent"})
*/
class AdminModule
{
// private vars, getters, setters
}
?>
<?php
namespace PageBundle\Entity;
use AdminBundle\Entity\AdminModule;
/**
* Page Component
*
* #ORM\Entity
* #ORM\Table(name="page_module")
*/
class PageModule extends AdminModule
{
//
}
?>
The issue I have, I think, is that the AdminModule annotation #ORM\DiscriminatorMap({"page" = "\PageBundle\Entity\PageModule"}) requires definition on the AdminBundle - but the PageBundle may not be installed.
I believe must have the wrong type of inheritance structure (?) however I am not clear on what alternative approaches I can take? Thanks for any help or direction :)
you can't do what you're trying to with table inheritance mappings,
because you have to write annotations in the parent class, so the parent class itself ends up being coupled with his children.
what you could use is a mapped superclass (#MappedSuperclass) to extend the actual parent entities from.
all your common properties should then go into the mapped superclass, using its children as actual entities to define different inheritance mappings and associations (association mappings in mapped superclasses are very limited).
so in your specific case you could have such a structure:
/**
* I'm not an actual Entity!
*
* #MappedSuperClass */
Class ModuleSuperClass {}
/**
* I don't have children
*
* #ORM\Entity */
Class BaseModule extends ModuleSuperClass {}
/**
* I have children
*
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"page" = "Page"})
*/
Class AdminModule extends ModuleSuperClass {}
/**
* I'm just a child
*
* #ORM\Entity
*/
Class PageModule extends AdminModule {}
your mileage may of course vary, i.e. I would rather have a BaseModule class without children, and then a BaseModule in an entirely different namespace to extend both AdminModule and PageModule from.
Related
I have entities that make use of Inheritance Mapping Doctrine inheritance. I have a custom identifier that I generate with #ORM\PrePersist(), which is in a trait and this is used in the parent class.
I want to be able to update properties that the child class has, for this reason, I need to run endpoints on the child entity
When I run an item operation, api platform can't find the resource.
PATCH /api/childas/{hash}
NotFoundHttpException
Not Found
api platform, it doesn't recognize hash as identifier. Take the id as your identified, even if it is false and hash is true.
Trait to generate hashes with which I identify the resource
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use Doctrine\ORM\Mapping as ORM;
trait HashableTrait
{
/**
* #ORM\Column(type="string", length=255)
* #ApiProperty(identifier=true)
*/
private $hash;
public function getHash(): ?string
{
return $this->hash;
}
/**
* #ORM\PrePersist()
*/
public function setHash()
{
$this->hash = \sha1(\random_bytes(10));
}
}
Parent class, is the table where the hash will be stored
<?php
namespace App\Entity;
use App\Entity\HashableTrait;
/**
* #ORM\HasLifecycleCallbacks()
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="integer")
* #ORM\DiscriminatorMap({
* 1 = "App\Entity\ChildA",
* 2 = "App\Entity\ChildB"
* })
*/
class Parent
{
use HashableTrait;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #ApiProperty(identifier=false)
*/
private $id;
public function getId(): ?int
{
return $this->id;
}
// Properties, setters, getters
}
Child class, on which I want to perform operations, such as updating some property that belongs to this class
<?php
namespace App\Entity;
class ChildA extends Parent
{
// Custom properties for ChildA
}
Config api platform operations for entity Child
App\Entity\ChildA:
collectionOperations:
post: ~
itemOperations:
post: ~
get: ~
patch: ~
delete: ~
I have thought about using data providers, but I keep getting the error.
The error was because both the hash property in the trait and the id property in the parent entity must be accessible from the entity to use.
Doctrine ORM uses reflection class to get information about attributes and their annotations. ReflectionClass::hasProperty obviously does not allow viewing private properties in the parent class.
I'm facing an issue concerning OneToOne relation between some Entities.
The subtlety is that the owner side can be multiple classes (all children of the same class).
Here is the summary
I've got Process which have several children entity classes (let's take ProcessPassport, ProcessIdentityCard and ProcessCadaster for example)
Some of my children entities need a relation with another entity TaxStamp, but not all of them. So I cannot put this association in the Process main entity.
Instead I created a trait TaxStampTrait, containing my relation mapping.
But this does not work properly :
I cannot define multiple possibilities for the association on the TaxStamp entity
Doctrine tells me there are error in my annotations for the relation, because I refer to the Process (legit since only some entities use the TaxStampTrait)
Question: what should I do to make it all work the right way?
Here is the gist with a summary of the code and classes: https://gist.github.com/bastos71/8e15f69ebecf5e97dc75187d130fe109
<?php
/**
* #ORM\Entity(repositoryClass="ProcessRepository")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
*/
class Process
{
// ...
}
<?php
/**
* #ORM\Entity(repositoryClass="ProcessCadasterRepository")
*/
class ProcessCadaster extends Process implements EntityWithTaxStampInterface
{
use TaxStampTrait;
// ...
}
<?php
/**
* #ORM\Entity(repositoryClass="ProcessIdentityCardRepository")
*/
class ProcessIdentityCard extends Process implements EntityWithTaxStampInterface
{
use TaxStampTrait;
// ...
}
<?php
/**
* #ORM\Entity(repositoryClass="ProcessPassportRepository")
*/
class ProcessPassport extends Process implements EntityWithTaxStampInterface
{
use TaxStampTrait;
// ...
}
<?php
/**
* #ORM\Entity(repositoryClass="TaxStampRepository")
*/
class TaxStamp
{
/**
* #ORM\OneToOne(targetEntity="Advercity\AdminBundle\Entity\Process", mappedBy="taxStamp")
*/
private $process;
// ...
}
<?php
trait TaxStampTrait
{
/**
* #ORM\OneToOne(targetEntity="TaxStamp", cascade={"persist"})
* #ORM\JoinColumn(name="tax_stamp_id", referencedColumnName="id")
*/
private $taxStamp;
// ...
}
I get unnecessary queries then entity has ManyToOne relationship with abstract class.
My classes structure:
/**
* #ORM\Entity
* #ORM\Table(name="tb_payment_info")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="integer")
* #ORM\DiscriminatorMap({
* "0" = "PaymentInfoPaypal",
* "1" = "PaymentInfoSkrill",
* })
*/
abstract class AbstractPaymentInfo
{
/**
* #ORM\Column(name="payment_info_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
}
/**
* #ORM\Entity
* #ORM\Table(name="tb_payment_info_paypal")
*/
class PaymentInfoPaypal extends AbstractPaymentInfo
{
}
/**
* #ORM\Entity
* #ORM\Table(name="tb_payment_info_skrill")
*/
class PaymentInfoSkrill extends AbstractPaymentInfo
{
}
My Payout class contains payment_info_id column from tb_payment_info table.
/**
* #ORM\Entity
* #ORM\Table(name="tb_payout")
*/
class Payout
{
/**
* #var AbstractPaymentInfo
*
* #ORM\ManyToOne(targetEntity="AbstractPaymentInfo")
* #ORM\JoinColumn(name="payment_info_id", referencedColumnName="payment_info_id")
*/
private $paymentInfo;
}
When I try to get any Payout entity, its paymentInfo initialize automatically. So:
$this->getEntityManager()->getRepository('TuoPayBundle:Payout')->find(255);
got 2 queries: first for Payout and second for its paymentInfo
$this->getEntityManager()->getRepository('TuoPayBundle:Payout')->findBy(['id'=>[255,256]]);
got 3 queries: first for Payout and second, third separate queries to init paymentInfo
How to achieve lazy load?
You cannot declare an abstract class in Doctrine 2 with #ORM\Entity notation. If you want to use abstract classes in your object model I suggest you check the documentation on Mapped Superclasses on how to do that correctly.
Most importantly you should declare the class with a special #ORM\MappedSuperClass annotation.
Keep in mind that Mapped superclasses come with restrictions. I quote:
A mapped superclass cannot be an entity, it is not query-able and persistent relationships defined by a mapped superclass must be unidirectional (with an owning side only). This means that One-To-Many associations are not possible on a mapped superclass at all. Furthermore Many-To-Many associations are only possible if the mapped superclass is only used in exactly one entity at the moment. For further support of inheritance, the single or joined table inheritance features have to be used.
I have one table for which I am creating two entities.
One entity is used in my one core bundle which is used for many projects.
For new project I am creating new bundle and I want to add one more column in that entity.
Can anyone please guide me how can I override entity in other bundle.
You can extend your original User entity to something like this.
namespace XXXX;
use Doctrine\ORM\Mapping as ORM;
use XXXXX as BaseUser;
/**
* #ORM\Entity
* #ORM\Table(name="User")
*/
class User extends BaseUser
{
/**
* #var string
*
* #ORM\Column(name="newProperty", type="string", nullable=false)
*/
private $newProperty;
.....
.....
}
This will have all properties from your class BaseUser and you can have additional properties in this new class that you are trying to create. You can add set and get methods here too.
I have entities
<?php
namespace Proj\Bundle\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* ClassTop
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Proj\Bundle\MyBundle\Repository\ClassTopRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="aVal", type="string")
* #ORM\DiscriminatorMap({ "ValOne" = "ClassSubOne", "ValTwo" = "ClassSubTwo", "ValThree" = "ClassSubThree" })
*
*/
class ClassTop
{
.....
}
class ClassSubOne extends ClassTop
{
....
}
class ClassSubTwo extends ClassTop
{
....
}
class ClassSubThree extends ClassTop
{
....
}
My problem is when I call the find() (or findOneBy, findAll, etc) method, I have an Oracle error.
$entityManager->getRepository('ProjMyBundle:ClassSubOne')->findAll()
In the query I have
SELECT field, field2 FROM CLASSTOP WHERE aVAL IN ()
The discriminator value "ValOne" is not mapping and not passed in Doctrine.
Any help would be much appreciated.
You need to repository classes for the subclasses not the top one. You treat your subclasses as full defined classes and that's all. Then when you need to query for your entities you don't go for the top entity but the subclasses. I don't know if I made myself clear here!
In your case:
#ORM\Entity(repositoryClass="path\to\your\repository")
The repository line goes for the subclasses not the top superclass. so you do
/**
*
* #ORM\Entity(repositoryClass="path\to\your\ClassSubOneRepository")
*/
class ClassSubOne extends ClassTop
{
...
}
instead of
/**
*
* #ORM\Entity(repositoryClass="Proj\Bundle\MyBundle\Repository\ClassTopRepository")
*/
class ClassTop
{
...
}