Doctrine OneToOne relation bidirectional - symfony

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;
// ...
}

Related

API PLATFORM custom identifier in parent class/table

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.

Custom base entity in Symfony 4

I am using Symfony 4 and with Doctrine where I have entities which have the same common attributes such as createdWhen, editedWhen, ...
What i would like to do is this:
Defining a kind of base entity that holds these common attributes and implements the setter and getter. And many entities which inherit from that base entity. The database fields should all be defined in the table of the respective sub entity (no super table or the like should be created in the db).
Example:
/**
* #ORM\Entity(repositoryClass="App\Repository\BaseRepository")
*/
class Base
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=127, nullable=true)
*/
private $createdWhen;
// Getter and setter
...
}
/**
* #ORM\Entity(repositoryClass="App\Repository\PersonRepository")
*/
class Person extends Base
{
/**
* #ORM\Column(type="string", length=127, nullable=true)
*/
private $name;
// Getter and setter
...
}
/**
* #ORM\Entity(repositoryClass="App\Repository\CarRepository")
*/
class Car extends Base
{
/**
* #ORM\Column(type="string", length=127, nullable=true)
*/
private $brand;
// Setter and getter
...
}
This should create the tables "person" and "car" (each with id, created_when) but no table base.
I would still like to be able to use the bin/console make:migration for updating the database schema.
Is this kind of approach possible with Symfony 4? If yes how would I define the entities and what do I have to change in terms of configuration, etc.?
You are looking for entity inheritance
Rewrite your code like so
/** #MappedSuperclass */
class Base
{
...
}
In fact, this is a part of Doctrine, here is what an official documentation says
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.

Symfony and Doctrine: cross database relations

I have two entities Entity1 and Entity2 with a OneToMany relation, but they live in two MySQL databases.
How can I implement those entities with their relation in Symfony?
Is it possible to create two separated bundles where to implement those entities?
In Doctrine, joining data across databases is not technically “supported” by a designed feature, but you can make it work by tricking Doctrine a little bit.
If you want to build a relationship between entities then they must use the same connection: same database.
The key to getting multiple databases to work is within your entity classes, you need to specify the table name of the entity with a prefix of the name of the database to which the table belongs. Here is an example using annotations:
<?php
namespace Demo\UserBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* Demo\UserBundle\Entity\User
*
* #ORMTable(name="users.User")
*/
class User implements
{
/* ... */
}
and
<?php
namespace Demo\PostBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* Demo\PostBundle\Entity\Post
*
* #ORMTable(name="posts.Post")
*/
class Post implements
{
/* ... */
}
and the relation table:
<?php
namespace Demo\PostBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* Demo\PostBundle\Entity\Post
*
* #ORMTable(name="posts.Post")
*/
class Post implements
{
/**
* #ORM\ManyToOne(targetEntity="\Demo\UserBundle\Entity\User")
**/
private $user;
/* ... */
/**
* Set user
*
* #param \Demo\UserBundle\Entity\Site $site
* #return Post
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \Demo\UserBundle\Entity\Site
*/
public function getUser()
{
return $this->user;
}
}
Here an article about it.
Hope this help

How do you define a "one to many" doctrine association that allows the user to add a new "one"?

I'm trying to define my doctrine entities for use with Symfony2 and am am unclear how about to define an association. The below example is for customers compiling a wishlist of components they would like for a custom-made bicycle.
A BikeEnquiry object can:
only ever take one frame
by default takes two wheels (but could be zero or more)
However, regarding the accessories, I'm less sure about how these should be mapped.
The user should be able to select from a dropdown of accessories to see all the items we have in the database - however, they should also be able to manually enter a description of what they are looking for if it does not exist (such as a new product on the market). I would like this new accessory then be added to the Accessory database table - it would also be good to have it flagged up as an unvalidated item for my review.
Below is my attempt as defining the entities and annotation associations:
use Doctrine\Common\Collections\ArrayCollection;
/** #Entity **/
class BikeEnquiry
{
private $BikeEnquiryid;
private $frameid;
/**
* #OneToMany(targetEntity="Wheel", mappedBy="wheelid")
**/
private $wheelid;
/**
* #ManyToMany(targetEntity="Accessory")
* #JoinTable(name="BikeEnquires_accessories",
* joinColumns={#JoinColumn(name="BikeEnquiryid", referencedColumnName="BikeEnquiryid")},
* inverseJoinColumns={#JoinColumn(name="accessoryid", referencedColumnName="accessoryid", unique=true)}
* )
**/
private $accessories;
public function __construct() {
$this->accessories = new ArrayCollection();
}
}
/** #Entity **/
class Frame
{
/**
* #OneToOne(targetEntity="BikeEnquiry")
* #JoinColumn(name="BikeEnquiryid", referencedColumnName="BikeEnquiryid")
**/
private $frameid;
}
/** #Entity **/
class Wheel
{
/**
* #ManyToOne(targetEntity="BikeEnquiry")
* #JoinColumn(name="BikeEnquiryid", referencedColumnName="BikeEnquiryid")
**/
private $wheelid;
}
/** #Entity **/
class Accessory
{
private $accessoryid;
}

Doctrine Class Table Inheritance with Symfony2

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.

Resources