I think the core of your question is language agnostic.
I have little PHP-specific experience here, but the separation of entities and the ORM is something I did tackle this week in our project.
You're correct in identifying that separating the entity from the ORM is a good decision. Your entities define your data structure, whereas your ORM defines where (and how) to store the data. These are separate things, and it's better to separate them (at least in layers, ideally in projects).
Working with C#/Entity Framework, this is very simple, since I can directly reuse the entity class as the ORM class, I don't need to redefine it. However, since you need ORM-specific logic/properties, you do need a separate ORM class to account for that. Composition, which you've used, seems to the better approach here.
However, I get the feeling that you've inverted your approach. Since your entity is dependent on your ORM model, that means that you haven't actually made your entity ORM-agnostic. If you change your ORM, you'll need to update your entities.
Think of it this way: what if your entities were being saved in two data locations (using different ORMs) at the same time? Your entity would contain a model for every ORM. There will be a lot of difficulty in deciding where to get your data from (model A? model B? Check if they are the same?)
I would do this the other way: the Model contains the Entity. This makes more sense, as having different ORMs would then each have their own Model (which contains the same Entity object).
This makes more sense: the ORM-spefic logic/properties are merely window dressing, they are tacked on to make the ORM able to handle the entity.
The entity is the core of your data. The ORM uses the entities. The entity does not use the ORM.
There are several benefits to approach it this way:
- Once your ORM has done its job, you simply take
myModel.Entity and discard
myModel itself. This is the equivalent of taking a candy bar out of its wrapper (which was only needed before eating the candy bar), because you'll want to be able to throw away the wrapper before you eat (it'd be silly if you were required to hold the wrapper while eating the candy bar).
- If you later switch ORM, or split your entities over multiple ORMs, the cost of switching over is reduced. You only need to change the ORM layer, and you can leave the Entity layer untouched. To extend the candy bar analogy: if Mars wants to redesign the Mars bar wrappers, they shouldn't be required to change the Mars bar itself to fit the new wrapper! The new wrapper needs to accommodate the existing Mars bar.