DDC-659: Programmatic PHP ClassMetadata Builder #814

Closed
opened 2026-01-22 12:51:22 +01:00 by admin · 9 comments
Owner

Originally created by @doctrinebot on GitHub (Jun 28, 2010).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user @beberlei:

Currently using the two existing PHP Metadata Drivers you have a hard time writing the metadata against a plain ClassMetadata instance, because its already so optimiized in the datastruct. It would we cool if we had a class (Doctrine\ORM\Tools\ClassMetadataBuilder) which accepts a ClassMetadata instance in the constructor and has convenience methods to build up this instance.

This way you could use the PHPStatic Driver for example:

class MyEntity
{
  static public function loadClassMetadata(ClassMetadata $metadata)
  {
    $builder = new ClassMetadataBuilder($metadata);
    $builder->setTable('my_entity');
    // ...
  }
}
Originally created by @doctrinebot on GitHub (Jun 28, 2010). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user @beberlei: Currently using the two existing PHP Metadata Drivers you have a hard time writing the metadata against a plain ClassMetadata instance, because its already so optimiized in the datastruct. It would we cool if we had a class (Doctrine\ORM\Tools\ClassMetadataBuilder) which accepts a ClassMetadata instance in the constructor and has convenience methods to build up this instance. This way you could use the PHPStatic Driver for example: ``` class MyEntity { static public function loadClassMetadata(ClassMetadata $metadata) { $builder = new ClassMetadataBuilder($metadata); $builder->setTable('my_entity'); // ... } } ```
admin added the New Feature label 2026-01-22 12:51:22 +01:00
admin closed this issue 2026-01-22 12:51:24 +01:00
Author
Owner

@doctrinebot commented on GitHub (Aug 8, 2010):

Comment created by @beberlei:

API Suggestion:

class ClassMetadataBuilder
{
    public function **construct(ClassMetadata $cm);
    public function markMappedSuperclass();

    public function setTable($name);
    public function addTableIndex($columns, $name);
    public function addTableUniqueConstraint($columns, $name);

    public function setJoinedTableInheritance();
    public function setSingleTableInheritance();
    public function setDiscriminatorColumn($name, $type);
    public function addDiscriminatorMapClass($name, $class);

    public function setChangeTrackingPolicyDeferredImplict();
    public function setChangeTrackingPolicyDeferredExplicit();
    public function setChangeTrackingPolicyNotify();

    public function addField($fieldName, $type, array $options = array());
    public function addVersionField($fieldName, $type, array $options = array());
    public function addPrimaryField($fieldName, $type, array $options = array());
    public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1);

    public function addOneToOne(fieldName, $targetEntity);
    public function addInverseManyToOne(fieldName, $targetEntity);
    public function addManyToOne(fieldName, $targetEntity);
    public function addOneToMany(fieldName, $targetEntity);
    public function addManyToMany(fieldName, $targetEntity);

    public function addLifecycleEvent($event, $methodName);
}

class FieldBuilder
{
    public function length($length);
    public function nullable($flag);
    public function unique($flag);
    public function columnName($name);
    public function precision($p);
    public function scale($s);
    public function columnDefinition($def);
}

class AssociationBuilder
{
    public function mappedBy($fieldName);
    public function inversedBy($fieldName);

    public function addCascade(array $cascade);
    public function cascadePersist();
    public function cascadeRemove();
    public function cascadeMerge();
    public function cascadeDetach();
    public function cascadeRefresh();

    public function notRemoveOrphans();
    public function removeOrphans();
    public function fetchEager();
    public function fetchLazy();

    public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null);
}

class OneToManyAssociationBuilder extends AssociationBuilder
{
    public function addOrderBy($fieldName, $orientation);
}

class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder
{
    public function setJoinTable($name);
    public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null);
}
@doctrinebot commented on GitHub (Aug 8, 2010): Comment created by @beberlei: API Suggestion: ``` class ClassMetadataBuilder { public function **construct(ClassMetadata $cm); public function markMappedSuperclass(); public function setTable($name); public function addTableIndex($columns, $name); public function addTableUniqueConstraint($columns, $name); public function setJoinedTableInheritance(); public function setSingleTableInheritance(); public function setDiscriminatorColumn($name, $type); public function addDiscriminatorMapClass($name, $class); public function setChangeTrackingPolicyDeferredImplict(); public function setChangeTrackingPolicyDeferredExplicit(); public function setChangeTrackingPolicyNotify(); public function addField($fieldName, $type, array $options = array()); public function addVersionField($fieldName, $type, array $options = array()); public function addPrimaryField($fieldName, $type, array $options = array()); public function setSequenceGenerator($sequenceName, $allocationSize = 1, $initialValue = 1); public function addOneToOne(fieldName, $targetEntity); public function addInverseManyToOne(fieldName, $targetEntity); public function addManyToOne(fieldName, $targetEntity); public function addOneToMany(fieldName, $targetEntity); public function addManyToMany(fieldName, $targetEntity); public function addLifecycleEvent($event, $methodName); } class FieldBuilder { public function length($length); public function nullable($flag); public function unique($flag); public function columnName($name); public function precision($p); public function scale($s); public function columnDefinition($def); } class AssociationBuilder { public function mappedBy($fieldName); public function inversedBy($fieldName); public function addCascade(array $cascade); public function cascadePersist(); public function cascadeRemove(); public function cascadeMerge(); public function cascadeDetach(); public function cascadeRefresh(); public function notRemoveOrphans(); public function removeOrphans(); public function fetchEager(); public function fetchLazy(); public function addJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null); } class OneToManyAssociationBuilder extends AssociationBuilder { public function addOrderBy($fieldName, $orientation); } class ManyToManyAssociationBuilder extends OneToManyAssociationBuilder { public function setJoinTable($name); public function addInverseJoinColumn($columnName, $referencedColumnName, $nullable = true, $unique = false, $onDelete = null, $columnDef = null); } ```
Author
Owner

@doctrinebot commented on GitHub (Aug 23, 2010):

Comment created by avalanche123:

I'm on it, in case you were wondering:)

@doctrinebot commented on GitHub (Aug 23, 2010): Comment created by avalanche123: I'm on it, in case you were wondering:)
Author
Owner

@doctrinebot commented on GitHub (Aug 24, 2010):

Comment created by @beberlei:

Can we discuss on the API?

@Roman what is your opinion on using set/add or omitting it in this case? I want the interface to be as fluent as possible :-)

@doctrinebot commented on GitHub (Aug 24, 2010): Comment created by @beberlei: Can we discuss on the API? @Roman what is your opinion on using set/add or omitting it in this case? I want the interface to be as fluent as possible :-)
Author
Owner

@doctrinebot commented on GitHub (Aug 26, 2010):

Comment created by romanb:

I think omitting add/set prefixes is fine if it improves the readability of the fluent interface.

@doctrinebot commented on GitHub (Aug 26, 2010): Comment created by romanb: I think omitting add/set prefixes is fine if it improves the readability of the fluent interface.
Author
Owner

@doctrinebot commented on GitHub (Aug 30, 2010):

Comment created by avalanche123:

So I'm kinda stuck with associations.
API like addOneToOne($fieldName, $targetEntity) makes little sense, since relation mapping requires much more information than that.
So one way to account for it would be to change the function signature to read addOneToOne($fieldName, $targetEntity, $associationMapping), where the mapping array would contain the rest of the required information like joinColumns, mappedBy, cascade, fetch, etc.
Then the $associationMapping array could be generated using the AssociationBuilder and subclasses.
This makes builder somewhat bloated...
Thoughts?

@doctrinebot commented on GitHub (Aug 30, 2010): Comment created by avalanche123: So I'm kinda stuck with associations. API like addOneToOne($fieldName, $targetEntity) makes little sense, since relation mapping requires much more information than that. So one way to account for it would be to change the function signature to read addOneToOne($fieldName, $targetEntity, $associationMapping), where the mapping array would contain the rest of the required information like joinColumns, mappedBy, cascade, fetch, etc. Then the $associationMapping array could be generated using the AssociationBuilder and subclasses. This makes builder somewhat bloated... Thoughts?
Author
Owner

@doctrinebot commented on GitHub (Aug 31, 2010):

Comment created by @beberlei:

The idea is that methods on ClassMetadataBuilder return the FieldBuilder and AssociationBuilder when necessary.

That way the interface isnt fully fluent, but it is fluent for the definition of one field, which is a pretty good separation layer anyways:

$builder->oneToOne("address", "MyEntities\Address")->cascadePersist()->cascadeMerge();
$builder->field("name", "string")->length(10);

Passing options is rather bad in my opinion, because it would be cumbersome to map that into a correct method call.

However I just realize that is complicated to call the final method on ClassMetadataInfo.

Can we collaborate in IRC on that tonight?

@doctrinebot commented on GitHub (Aug 31, 2010): Comment created by @beberlei: The idea is that methods on ClassMetadataBuilder return the FieldBuilder and AssociationBuilder when necessary. That way the interface isnt fully fluent, but it is fluent for the definition of one field, which is a pretty good separation layer anyways: ``` $builder->oneToOne("address", "MyEntities\Address")->cascadePersist()->cascadeMerge(); $builder->field("name", "string")->length(10); ``` Passing options is rather bad in my opinion, because it would be cumbersome to map that into a correct method call. However I just realize that is complicated to call the final method on ClassMetadataInfo. Can we collaborate in IRC on that tonight?
Author
Owner

@doctrinebot commented on GitHub (Oct 22, 2010):

Comment created by shurakai:

Hm, what about a "setIdGenerator" method?

@doctrinebot commented on GitHub (Oct 22, 2010): Comment created by shurakai: Hm, what about a "setIdGenerator" method?
Author
Owner

@doctrinebot commented on GitHub (Sep 4, 2011):

Comment created by @beberlei:

Implemented

@doctrinebot commented on GitHub (Sep 4, 2011): Comment created by @beberlei: Implemented
Author
Owner

@doctrinebot commented on GitHub (Sep 4, 2011):

Issue was closed with resolution "Fixed"

@doctrinebot commented on GitHub (Sep 4, 2011): Issue was closed with resolution "Fixed"
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#814