Skip to content

Removing the metadata instantiation from the EM#7143

Closed
mikeSimonson wants to merge 23 commits intodoctrine:old-prototype-3.xfrom
mikeSimonson:extract-metadata-from-em-constructor
Closed

Removing the metadata instantiation from the EM#7143
mikeSimonson wants to merge 23 commits intodoctrine:old-prototype-3.xfrom
mikeSimonson:extract-metadata-from-em-constructor

Conversation

@mikeSimonson
Copy link
Contributor

@mikeSimonson mikeSimonson commented Mar 20, 2018

The metadata instantiation is kept in the create method for BC for now.
A new static method is added to be able to pass the metadata to the EM.

TODOs

  • Document BC Breakages
  • Add lazy collection implementation (populated as-we-go)
  • Ensure type aliases are correctly cached (create key copies in the metadata)
  • Ensure the \\ ltrim() is gone

The metadata instantiation is kept in the create method for BC for now.
A new static method is added to be able to pass the metadata to the EM.
@mikeSimonson
Copy link
Contributor Author

@Ocramius Is this what you meant when we discussed at phpbenelux ?
Is the next step refactoring all the call to create to the new createWithMetadata function ?

@Ocramius
Copy link
Member

@mikeSimonson the idea was to make the metadata a ClassMetadata[] lazy collection (injectable) rather than a ClassMetadataFactory - is this a middle-ground before we go there?

@mikeSimonson
Copy link
Contributor Author

I just wanted to make sure that I was going in the right direction.
I should pull lazymap as a dep then I suppose ?

@Ocramius
Copy link
Member

@mikeSimonson I'd suggest making it an eager map for now, and later we fix it with LazyMap

@mikeSimonson
Copy link
Contributor Author

Does the new commit goes in the right direction ?

Can I
get rid of the cache configuration in the EM constructor ?
change the behavior of getAllMetadata for simplicity ?
remove the initialize thingy in the ClassMetadataFactory ?

How do I proceed to remove the entity manager from the OnClassMetadataNotFoundEventArgs in the ClassMetadataFactory and LoadClassMetadataEventArgs ?

Any other remarks / comment / direction ?
@Ocramius

@Majkl578 Majkl578 added this to the 3.0 milestone Mar 23, 2018
/**
* @return MetadataCollection
*/
public function getMetadatas(): MetadataCollection
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would avoid exposing it here at all.

* @return MetadataCollection
*/
public function getConnection()
public function getMetadatas(): MetadataCollection
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid exposing this

public function getClassMetadata($className) : Mapping\ClassMetadata
{
return $this->metadataFactory->getMetadataFor($className);
$className = StaticClassNameConverter::getRealClass($className);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can likely be dealt with in a MetadataCollection decorator instead

$this->conn = $conn;
$this->config = $config;
$this->eventManager = $eventManager;
$this->metadatas = $metadataCollection;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

metadata is irregular: the plural of metadata is metadata. A better way to name this is probably mappings

try{
$this->metadatas->get(StaticClassNameConverter::getClass($value));
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
} catch (\Exception $e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most likely wrong here

try{
$this->metadatas->get(StaticClassNameConverter::getClass($value));
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
} catch (\Exception $e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likely wrong here - why did you introduce these try-catches? I know you are still playing around with the patch: just wondering why this got added to make things work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just because for now get throws an Exception if the metadata is not found where it wasn't before.

return new EntityManager($connection, $config, $connection->getEventManager());
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
/** @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory */
$metadataFactory = new $metadataFactoryClassName($config, $connection, $eventManager);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 this is a decent BC-compliant approach for now :)

return $this->classMetadatas[$name];
}

public static function fromClassMetadatas(array $classMetadatas)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using (ClassMetadata $firstClass, ClassMetadata ...$otherClasses) here, which prevents instantiation if no metadata is available at all.


public function get($name)
{
if(!isset($this->classMetadatas[$name])){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add the class name normalisation here


private function __construct(array $classMetadatas)
{
$metadataByClassName = array_reduce (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is simpler with:

array_combine(
    array_map(function (ClassMetadata $metadata) { return $metadata->getClassName(); }, $metadata),
    $metadata
);

@Ocramius
Copy link
Member

Does the new commit goes in the right direction ?

👍

Can I get rid of the cache configuration in the EM constructor ?

Most likely. Metadata caching can be moved to a CachedMetadataCollection or such

change the behavior of getAllMetadata for simplicity ?

That is a subtle BC break: please don't change that.

remove the initialize thingy in the ClassMetadataFactory ?

Yes: since metadata now has a lifecycle that is completely separate from the ORM itself, removal is fine here 👍

How do I proceed to remove the entity manager from the OnClassMetadataNotFoundEventArgs in the ClassMetadataFactory and LoadClassMetadataEventArgs ?

I suggest we completely remove these events, as the user can simply decorate the MetadataCollection in order to manipulate metadata information

Any other remarks / comment / direction ?

Provided above :-)

return $this->metadata[$name];
}

public static function fromClassMetadatas(ClassMetadata $firstClass, ClassMetadata ...$otherClasses)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a static instead of constructor directly?

Also the signature is ugly, let's just have __construct(ClassMetadata ...$metadata) with assert(count($metadata) !== 0) if it really MUST be non-empty.


default:
$class = $this->config->getCustomHydrationMode($hydrationMode);
$class = $this->config->getCustomHydrationMode((string) $hydrationMode);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is most likely wrong, see #7130 (comment). It could be int|string currently.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCustomHydrationMode expect a string in it's function definition.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this in my PR since it's really int|string now. Something to re-do later on probably.

@mikeSimonson
Copy link
Contributor Author

@Ocramius My main question now is to know if passing the whole mapping collection to the BasicEntityPersister is the way to go ? (commit extract the MetadataFactory from the basic entity persister and the following one)

Copy link
Member

@Ocramius Ocramius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ocramius My main question now is to know if passing the whole mapping collection to the BasicEntityPersister is the way to go ? (commit extract the MetadataFactory from the basic entity persister and the following one)

@mikeSimonson I'd suggest that any component requiring metadata should just be given the collection. The MetadataCollection is now to be considered a "dumb" immutable structure, so it should be fine to pass it around.

Obviously, if only one entity metadata is required in a persister, then it should be passed in explicitly, but I don't know how big that change would be.

It will be easy to extract these scenarios when looking at classes that always get() on the collection with the same parameter.

try{
$this->mappings->get($value);
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
} catch (\Exception $e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to get rid of these later on.

foreach ($id as $i => $value) {
if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
if (is_object($value)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API for getting metadata by object or string should be separate

if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
$scalarId[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
if (is_object($value)) {
try{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here - try-catch block needs to go

if (is_object($value) && $this->metadataFactory->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
$id[$i] = $this->unitOfWork->getSingleIdentifierValue($value);
if (is_object($value)) {
try{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same: try-catch should be removed overall. We possibly need a better abstraction.


public function get(string $name) : ClassMetadata
{
$name = StaticClassNameConverter::getRealClass($name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider checking for existence before calling StaticClassNameConverter. Copying a metadata reference into a different key is also a good idea for performance reasons

*/
private $metadata;

private function __construct(array $metadata)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document this please

$this->metadata = $metadataByClassName;
}

public function get(string $name) : ClassMetadata
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The various APIs getting class metadata by object reference could need a getByObject(object $object)


$config->setMetadataDriverImpl(
$mappingDriver ?? $config->newDefaultAnnotationDriver([
realpath(__DIR__ . '/Models/CMS'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering why this test changed to load everything - is this because we don't yet have a lazy collection?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because I am pocking around a bit everywhere to try and fix everything that I broke.

@Ocramius Ocramius assigned mikeSimonson and unassigned Ocramius Apr 13, 2018
@Ocramius
Copy link
Member

@mikeSimonson I've updated the PR description with the TODOs matching the discussion above.

@Majkl578
Copy link
Contributor

Majkl578 commented May 4, 2018

@mikeSimonson Do you plan to continue with this?

@mikeSimonson
Copy link
Contributor Author

@Majkl578 yes. I don't have much time right now but I hope that I will have it in a good state for amsterdamphp

Base automatically changed from master to old-prototype-3.x February 23, 2021 08:18
@derrabus
Copy link
Member

I'm closing this PR because it hasn't seen progress for several years. Please open a new one if you want to pick up the work one day.

@derrabus derrabus closed this May 11, 2022
@derrabus derrabus removed this from the 3.0.0 milestone May 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants