What's New
ObjectQuel 2.0 corrects the naming of annotations and parameters that were borrowed from Doctrine but meant different things in ObjectQuel, and tightens the via clause in the query language.
Relationship Annotations
The most significant change in 2.0 is the renaming of two annotations. In 1.x, @OneToMany and the inverse side of @OneToOne were borrowed from Doctrine and implied a relationship type. In ObjectQuel, the intent was always different — these were hydration markers, not relationship descriptors. 2.0 corrects the naming.
| 1.x | 2.0 | Notes |
|---|---|---|
@OneToMany |
@InverseOf |
Renamed — @OneToMany was a misnomer borrowed from Doctrine. The intent was always a hydration marker, not a relationship descriptor. |
@OneToOne (inverse side) |
@InverseOf |
Renamed — same reasoning as above. The inverse side of a one-to-one is also a hydration marker, not a relationship descriptor. |
@ManyToOne |
@ManyToOne |
Annotation unchanged, parameters renamed — see parameter renames below |
@OneToOne (owning side) |
@OneToOne |
Annotation unchanged, parameters renamed — see parameter renames below |
Why @InverseOf?
In ObjectQuel, relations are defined in exactly one place: the owning side. That is the entity that declares @ManyToOne or @OneToOne with a foreign key column. This is where ObjectQuel reads the relationship for persistence. There is no second definition on the other side.
@InverseOf is the opposite of that relation, but it participates in none of those operations. It is a hydration marker only — it tells ObjectQuel which property on this entity should receive the results when hydrating related entities. It has no influence on how the relation is stored or queried, which is why it carries no column mapping parameters.
In 1.x, @OneToMany and the inverse side of @OneToOne were borrowed from Doctrine, where both sides of a relationship carry meaning. In ObjectQuel that was never the case. @InverseOf makes the limited role of this annotation explicit.
Migrating from 1.x
The change is mechanical. Replace every @Orm\OneToMany with @Orm\InverseOf, and replace @Orm\OneToOne on the inverse side with @Orm\InverseOf. Rename mappedBy to relation in both cases.
// 1.x
class OrderEntity {
/**
* @Orm\OneToMany(targetEntity="OrderItemEntity", mappedBy="orderId")
*/
public EntityCollection $items;
}
// 2.0
class OrderEntity {
/**
* @Orm\InverseOf(targetEntity="OrderItemEntity", relation="order")
*/
public EntityCollection $items;
}
For one-to-one relationships, replace the inverse side only. The owning side (@Orm\OneToOne with a foreign key column) is unchanged:
// 1.x
class UserEntity {
/**
* @Orm\OneToOne(targetEntity="ProfileEntity", mappedBy="userId")
*/
public ?ProfileEntity $profile;
}
// 2.0
class UserEntity {
/**
* @Orm\InverseOf(targetEntity="ProfileEntity", relation="user")
*/
public ?ProfileEntity $profile;
}
Owning Side
@ManyToOne and the owning side of @OneToOne are not renamed, but their parameters changed. inversedBy is replaced by referencedColumn and relationColumn is replaced by localColumn:
// 1.x
class OrderItemEntity {
/**
* @Orm\ManyToOne(targetEntity="OrderEntity", inversedBy="id", relationColumn="orderId")
*/
private OrderEntity $order;
}
// 2.0
class OrderItemEntity {
/**
* @Orm\ManyToOne(targetEntity="OrderEntity", referencedColumn="id", localColumn="orderId")
*/
private OrderEntity $order;
}
// 1.x
class ProfileEntity {
/**
* @Orm\OneToOne(targetEntity="UserEntity", inversedBy="id", relationColumn="userId")
*/
private UserEntity $user;
}
// 2.0
class ProfileEntity {
/**
* @Orm\OneToOne(targetEntity="UserEntity", referencedColumn="id", localColumn="userId")
*/
private UserEntity $user;
}
Annotation Parameters
Several annotation parameters were also borrowed from Doctrine but meant different things in ObjectQuel. 2.0 renames them to reflect their actual purpose.
| 1.x | 2.0 | Annotation | Notes |
|---|---|---|---|
inversedBy |
referencedColumn |
@ManyToOne, @OneToOne |
In Doctrine, inversedBy names a property on the target entity. In ObjectQuel it always pointed to a foreign key column — referencedColumn reflects that. |
mappedBy |
removed | @OneToOne (inverse side) |
No longer needed. In 1.x it pointed to the key property on the other entity. @InverseOf uses relation instead, which names the relation property on the other entity. |
relationColumn |
localColumn |
@ManyToOne, @OneToOne |
Renamed to make clear this is the column on the local (owning) entity, not something inherited from Doctrine terminology. |
Before and After
// 1.x
class OrderItemEntity {
/**
* @Orm\ManyToOne(targetEntity="OrderEntity", inversedBy="id", relationColumn="orderId")
*/
private OrderEntity $order;
}
// 2.0
class OrderItemEntity {
/**
* @Orm\ManyToOne(targetEntity="OrderEntity", referencedColumn="id", localColumn="orderId")
*/
private OrderEntity $order;
}
// 1.x
class UserEntity {
/**
* @Orm\OneToOne(targetEntity="ProfileEntity", mappedBy="userId")
*/
public ?ProfileEntity $profile;
}
// 2.0
class UserEntity {
/**
* @Orm\InverseOf(targetEntity="ProfileEntity", relation="user")
*/
public ?ProfileEntity $profile;
}
Query Language: via Clause
The via clause no longer accepts arbitrary join expressions. It must reference a relation
property declared with @ManyToOne or @OneToOne on the range entity.
@InverseOf is also supported: the engine resolves the relation through the hydration
marker and inlines it automatically.
// 1.x — expression form (no longer supported)
range of y is UserEntity
range of x is ProductEntity via x.userId=y.id
// 2.0 — relation property
range of y is UserEntity
range of x is ProductEntity via y.products
Summary of Changes
Annotations renamed:
@Orm\OneToMany→@Orm\InverseOf@Orm\OneToOne(inverse side) →@Orm\InverseOf
Parameters renamed:
inversedBy→referencedColumn(on@ManyToOneand@OneToOne)relationColumn→localColumn(on@ManyToOneand@OneToOne)mappedBy→ removed (replaced byrelationon@InverseOf)
Query language:
viaclause now requires a relation property — arbitrary join expressions are no longer supported