Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Known-issue] Doctrine entities could not be woven #327

Open
TheCelavi opened this issue Jul 7, 2017 · 12 comments
Open

[Known-issue] Doctrine entities could not be woven #327

TheCelavi opened this issue Jul 7, 2017 · 12 comments

Comments

@TheCelavi
Copy link
Contributor

Doctrine entities can not be weaved - class metadata gets all messed up.

Engine generates Entity__AopProxied, which is copy of original class, and then generates class Entity that extends Entity__AopProxied and contains weaved methods/properties.

Issue is that Entity__AopProxied contains doctrine metadata, so Doctrine actually generates such metadata that Entity__AopProxied is main class and every relation is bound to that class, not Entity.

Side effects are that, per example, table name gets suffix "__aop_proixed", while relations gets all screwed up, so any join is broken because different aliases are generated with SQL walker. Lazy loading does not work as well.

Possible solution would be (in order to preserve BC compatibility) to provide possibility to specify different method of weaving for certain classes. Per example, in this case, in order to work as it should, Entity__AopProxied should not be generated and inherited, it should be generated only Entity class.

Yes, maybe we will loose nice debugging and breakpoints, however, for now, we can sacrifice that for working code.

@TheCelavi
Copy link
Contributor Author

Note - I am using annotations to map entities.

@TheCelavi
Copy link
Contributor Author

TheCelavi commented Jul 7, 2017

Tried to use different mapping method - just for testing purposes, no love. Which is reasonable, Entity__AopProxied is not valid superclass....

Here is the example of wrong SQL:

An exception occurred while executing 'SELECT t1.id AS id_2, t1.feed_item_id AS feed_item_id_3, t1.url AS url_4, t1.platform AS platform_5, t1.views AS views_6, t1.likes AS likes_7, t1.dislikes AS dislikes_8, t1.comments AS comments_9, t1.shares AS shares_10, t1.created_at AS created_at_11, t1.updated_at AS updated_at_12, t1.created_by AS created_by_13, t1.updated_by AS updated_by_14, t1.offer_id AS offer_id_15 FROM report_item t1 WHERE t0.offer_id = ?'

SQLSTATE[42S22]: Column not found: 1054 Unknown column 't0.offer_id' in 'where clause'").

Note how generated alias for report_item is t1, but for where statement is t0 ? This is simple, lazy loaded relation.

@TheCelavi
Copy link
Contributor Author

Ok, this does not allow me to sleep at all. By playing around, going trough code, I think that there is a workaround in for this situation, however, Doctrine mapping must be modified (tried with hardcoding just to test theory, works).

When Entity__AopProxied class is generated, its mapping data must exists and must be appropriately set. It should contain all Entity mapping data, with exception, it must be set as MappedSuperclass (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#mapped-superclasses). In that matter Entity__AopProxied would become a mapping superclass, while generated Entity would have all required metadata as concrete class with mapping info from superclass.

It is not just simple "replace annotation" process, Doctrine supports mapping via XML, YML, annotations and PHP...

So, in order to do so successfully, we have to find out a certain point of time, or a method, that we can modify, intercept, whatever, so we can do this modification.

Maybe @Ocramius, @beberlei (or someone else) could give us some pointers or advices how to do this properly...?

@Ocramius
Copy link

Ocramius commented Jul 8, 2017 via email

@TheCelavi
Copy link
Contributor Author

@Ocramius not that I have that choice, either way, it seams as easy to solve/add support for doctrine entities.

Only thing required is to modify ClassMetadata for __AopProxied entity. For what I have seen, it should be an easy pick with Doctrine's loadclassmetadata event - just to set isMappedSupperclass to true.

I will try that approach today, I think that it will work.

@TheCelavi
Copy link
Contributor Author

@lisachenko Ok, got it, here is a bastebin of listener that could handle this mapping issue with Doctrine entities which are weaved: https://pastebin.com/LwdJ0hXn

It gives a basis support, it works with my use case scenario, but in general, it is quite possible, Doctrine seams very flexible when it comes to metadata. I would create a PR for this project, an optional listener for Doctrine that can be used, if required.

For symfony bundle, I would add a compiler pass which would determine if there is a doctrine ORM included, and if that is true, it would auto register listener.

Feedback?

TheCelavi added a commit to RunOpenCode/framework that referenced this issue Jul 9, 2017
@lisachenko
Copy link
Member

@TheCelavi this issue is very cumbersome, yes. There is no place for double magic (goaop + doctrine), so yes, unicorns are expected )

Just to check one more idea, could we somehow use Proxies\__CG__ namespace to make doctrine trust our generated entities? /cc @Ocramius

@lisachenko
Copy link
Member

Closed via 6e2a0fe

@TheCelavi TheCelavi reopened this Jul 18, 2017
@TheCelavi
Copy link
Contributor Author

If we are dealing with complex object graph with bidirectional relations and several inheritance, metadata hierarchy gets really, really complex and things tends to go bad...

It seams that Doctrine entities could not be supported by manipulating metadata... I will have to investigate further more in regards to this topic....

This issue ought to be open.

@MatthieuSarter
Copy link

MatthieuSarter commented Jan 31, 2018

Hi,

FYI, there are still some issues with the workaround. I was experiencing the same t0/t1 issue in the requests, I added the workaround, it worked fine with most of the cases, but I still got an error with some entity relationships.

EntityA is representing sensors and EntityB is representing past sensor values. EntityB has a ManyToOne relation to EntityA, which works fine. But if I add a OneToMany relationship from EntityA to EntityB I get this error :

  [Doctrine\ORM\Mapping\MappingException]
  It is illegal to put an inverse side one-to-many or many-to-many association on mapped superclass 'Appli\Metier\Bundle\Entity\EntityB__AopProxied#pastValues'.

I'm using Go AOP to add execution traces around all the methods in the Appli namespace, using this annotation in my aspect:
@Around("execution(public|protected Appli\**->*(*))")

If I disable this annotation, everything works fine with the OneToMany relationship between EntityA and EntityB.

@lisachenko
Copy link
Member

Hi, @MatthieuSarter, if you don't need AOP logic for entities, then you could easily exclude all of them from weaving process by adding logical restriction into the pointcut:

@Around("execution(public|protected Appli\**->*(*)) && !within(Appli\**\*Entity*)")

Idea is to exclude all class entities by their namespace or Entity suffix in the name.

@MatthieuSarter
Copy link

Thanks @lisachenko !

It works fine this way :) And I can live with the fact of not having execution traces on the entities methods :)

@lisachenko lisachenko changed the title Doctrine entities can not be weaved [Known-issue] Doctrine entities could not be woven May 13, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

4 participants