Simple index definition in Doctrine 2 - php

Is there a simple way to define a non-unique index over a column?
When I define unique index, this is perfectly sufficient:
/** #ORM\Entity */
class Foo {
/** #ORM\Column(type="string", unique=true) */
private $foo;
}
However, for non-unique index, I need this bunch of boilerplate:
/**
* #ORM\Entity
* #ORM\Table(indexes={#Index(name="foo_idx", columns={"foo"})})
*/
class Foo
{
/** #ORM\Column(type="string") */
private $foo;
}
I'd prefer something like #ORM\Index annotation on the single property, or index=true etc...

I have to disappoint you...
#ORM\Table(indexes={#Index(name="foo_idx", columns={"foo"})})
is as simple as the annotation can get.
If $foo would be a relating entity it would be indexed automatically, but for indexing fields you need to add the index as you did.
You can find all #Column attributes here in the Doctrine 2 documentation. Sadly enough index is not among them.
Maybe you can make a feature request here on GitHub.

Related

Doctrine: how to describe manyToOne relation with composite key and update cascade?

I have a table that has composite primary key: id + est_date. And it has an entity:
class Parent
{
/**
* #ORM\Id
*/
private $id;
/**
* #ORM\Id
*/
private int $estDate;
...
}
Now I need to create a related table and its entity.
class Child
{
...
/**
* don't know what to write here
*/
private $parentId;
/**
* don't know what to write here
*/
private int $parentEstDate;
...
}
How to discribe relation ManyToOne (many "Child" entities may relate to 1 "Parent")? And the second issue is - "estDate" of the "Parent" may change. How to specify cascade update in "Child"?
Please don't write that doctrine doesn't recomment to use composite keys. I know that.
on the child-entity you would refer to the parent entity the same way as with single columns, essentially. Starting with
annotation version:
/**
* #ORM\ManyToOne(targetEntity=Parent::class)
*/
private ?Parent $parent;
since the child is the owning side, you have to provide join columns, as you have noticed. There is a badly documented annotation JoinColumns that allows to define multiple join columns. (Note for those using the attribute syntax instead: you should be able to have multiple #[JoinColumn(...)], without the JoinColumns-Wrapper)
annotation version:
/**
* #ORM\ManyToOne(targetEntity=Parent::class)
* #ORM\JoinColumns({
* #ORM\JoinColumn("parent_id", referencedColumnName="id"),
* #ORM\JoinColumn("parent_est_date", referencedColumnName="est_date")
* })
*/
private ?Parent $parent;
If you want to add the inverse side as well, you always reference the object property, not the columns when using mappedBy/inversedBy.
Generally with doctrine-orm: Your class/object should not care about columns, only about php stuff, doctrine should handle the rest. The annotations tell doctrine, how this converts to columns. So not every column will get its own property in this case.

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.

Best practise for placing custom Entity methods in Symfony

I'm creating Symfony project. And now I'm trying to find best practice for adding custom methods.. What is yours?
Visual explanation:
users table
id | name | surname
---+------+--------
1 | John | Smith
2 | Matt | Malone
Entity\User.php
namespace TestBundle\Entity\User;
use Doctrine\ORM\Mapping as ORM;
/**
* User
* #ORM\Table(name="users")
* #ORM\Entity
*/
class User
{
/**
* #ORM\Column(name="id", type="string", length=36)
* #ORM\Id
* #ORM\GeneratedValue(strategy="UUID")
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*/
private $name;
/**
* #ORM\Column(name="surname", type="string", length=255, nullable=true)
*/
private $surname;
/**
* OneToMany
*/
private $userCompanies;
{{ Setters and Getters }}
}
Where I should store custom method, like:
function getFullName()
{
return sprintf("%s %s", $this->getName(), $this->getSurname());
}
Or more complex:
function getCurrentUserCompany()
{
foreach ($this->getUserCompanies() as $company) {
if ($company->isActive()) {
return $company;
}
}
return null;
}
Please note, that all data returned via JSON
So far I tried extending class, but annotations not working as expected. Placing custom methods in same file looks trashy, since there will be more than one of them.
But.. but if there is repositoryClass - maby there is place for custom methods as well?
Thanks!
If it's about methods that are used mainly for display purposes then they are very similar to the getters, in my opinion they best fit is in the Entity itself, so inside your User.php class.
The repository is for defining methods for getting the entity from your storage level (DB, cache...), but the view level (your twig) should take the data from the entity itself.
If you need something more complicated or you need to reuse it, like a date filter, then it's better to create a Twig extension.
Methods like that belong to entity class and there is no reason to split code. If many entity classes share some methods, you can always create shared base abstract class or trait for them.
If you really want separated files for sake of your aesthetic, then use traits, but remember that it's not proper and conventional use of them.

Symfony2 / Doctrine mapped superclass in the middle of class table inheritance

I currently have a model structure as follows:
/**
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="related_type", type="string")
* #ORM\DiscriminatorMap({"type_one"="TypeOne", "type_two"="TypeTwo"})
*/
abstract class BaseEntity {
... (all the usual stuff, IDs, etc)
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="baseEntity")
*/
private $comments;
}
/**
* #ORM\Entity
*/
class TypeOne extends BaseEntity {
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\Column(type="string")
*/
private $description;
}
/**
* #ORM\Entity
*/
class TypeTwo extends BaseEntity {
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\Column(type="string")
*/
private $description;
}
/**
* #ORM\Entity
*/
class Comment {
... (all the usual stuff, IDs, etc)
/**
* #ORM\ManyToOne(targetEntity="BaseEntity", inversedBy="comments")
*/
private $baseEntity;
}
The idea here is to be able to tie a comment to any of the other tables. This all seems to be working ok so far (granted, I'm still exploring design options so there could be a better way to do this...), but the one thing I've noticed is that the subclasses have some common fields that I'd like to move into a common parent class. I don't want to move them up into the BaseEntity as there will be other objects that are children of BaseEntity, but that won't have those fields.
I've considered creating a MappedSuperclass parent class in the middle, like so:
/**
* #ORM\MappedSuperclass
*/
abstract class Common extends BaseEntity {
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\Column(type="string")
*/
private $description;
}
/**
* #ORM\Entity
*/
class TypeOne extends Common {}
/**
* #ORM\Entity
*/
class TypeTwo extends Common {}
I figured this would work, but the doctrine database schema generator is complaining that I can't have a OneToMany mapping on a MappedSuperclass. I didn't expect this to be a problem as the OneToMany mapping is still between the root BaseEntity and the Comment table. Is there a different structure I should be using, or other way to make these fields common without adding them on the BaseEntity?
From the Docs:
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.
That said, how can you associate one entity with one that is not?
More from the docs:
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
assocations 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.
Source: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html
Update
Because your MappedSuperClass extends BaseEntity it also inherits the BaseEntity's associations, as if it were its own. So you effectively DO have a OneToMany on a MappedSuperClass.
To get around it, well, you'd need to modify/extend doctrine to work the way you want.
As far as native functionality goes you have two options:
Class Table Inheritance
You Common class and the resulting DB representation would have the common fields and child classes will now only have the fields specific to themselves. Unfortunately this may be a misrepresentation of your data if you are simply trying to group common fields for the sake of grouping them.
Make Common an Entity
It appears that all a Mapped Super Class is is an Entity that isn't represented in the DB. So, make common a Entity instead. The downside is that you'll end up with a DB table, but you could just delete that.
I recommend that you take a second look at your data and ensure that you are only grouping fields if they are common in both name and purpose. For example, a ComputerBox, a ShoeBox, a Man, and a Woman may all have the "height" property but in that case I wouldn't suggest have a Common class with a "height" property that they all inherit from. Instead, I would have a Box with fields common to ComputerBox and ShoeBox and I'd have a Person with fields common to Man and Woman. In that situation Class Table Inheritance or single table if you prefer would work perfectly.
If your data follows that example go with Single Table or Class Table Inheritance. If not, I might advise not grouping the fields.

Doctrine2, Mapping "inherited" tables

as pointed out here: Doctrine 2.1 - Map entity to multiple tables Doctrine2 does not allow mapping of one object to multiple tables.
I currently have a Mysql db setup similar to this:
base_entity: id, some_basic_data_columns
state: id, state, entity_id (FK to base_entity.id), start_time, end_time, ...
entity_one: id (FK to base_entity.id), some_specific_data
entity_two: id (FK to base_entity.id), some_specific_data
and so on...
In a way, entity_x is "extending" base_entity, and all these entities can have multiple states. To have proper foreign keys I would have to either have separate state tables (which I don't want to do because they will structurally be the same ), or do it like this.
The base entity by itself is useless, id could even be boiled down to just the id field to allow to join with each child entity to multiple states.
I do not need a BaseEntity class, but I do need for each child Entity to have a getStates() method. Of course I may actually have an abstract entity class, but concrete entities will extend it, not have it as a property like they would if I would map them as one would map other one-to-one relationships
Since Doctrine will not allow me to map EntityOne to both entity_one and base_entity table I have to ask:
Is this bad design? Am I overlooking some other way to solve this elegantly? I know other DMBSs have inheritance, but for instance PostgreSql would still not allow me to join the base_entity to state if no physical base_entity exists for a child.
I could do something like this on the code side:
class EntityOne {
// baseEntity as a property
private $_baseEntity;
// private getter for the base table
private getBaseEntity();
// and getters like this for properties in the base table
public getStates(){
return $this->getBaseEntity()->getStates();
}
}
This way the entity would behave like a single entity (not combined from base and child) to the outside world, but it would still require that I write a separate BaseEntity class and all the config info to connect it to other entity classes
Basically, what I'm asking is: is this a Db design issue, and I got it completely wrong from the start (and if I did, which is the "best" approach), or is this a code issue, and I should work around it with code (if so, is my approach in 2. ok, or are there better ways to deal with this), and are there ORMs which allow for multiple table mapping?
Many thanks in advance.
You could use Class Table Inheritance (see Doctrine documentation about that), defining a BaseEntity entity class, and and create EntityOne and EntityTwo extending that.
You could define the relationship between the BaseEntity class and the State entity class as one-to-many association - if I understood right what you wanted, providing the needed getState() method in the BaseEntity class.
Something like this:
/**
* #Entity
* #Table(name="base_entity")
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="entity_type", type="string")
* #DiscriminatorMap({"entity_one"="EntityOne", "entity_two"="EntityTwo"})
*/
class BaseEntity {
/**
* #Id
* #Column(type="integer")
*/
protected $id;
/**
* #OneToMany(targetEntity="State", mappedBy="entity)
**/
protected $states;
public function getStates() {
return $this->states;
}
...
}
/**
* #Entity
* #Table(name="entity_one")
*/
class EntityOne extends BaseEntity {
...
}
/**
* #Entity
* #Table(name="entity_two")
*/
class EntityTwo extends BaseEntity {
...
}
/**
* #Entity
* #Table(name="state")
*/
class State {
/**
* #ManyToOne(targetEntity="BaseEntity", inversedBy="states")
* #JoinColum(name="entity_id", referencedColumnName="id")
*/
protected $entity;
public function getEntity() {
return $this->entity;
}
...
}

Categories