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.

explanation

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:

  • inversedByreferencedColumn (on @ManyToOne and @OneToOne)
  • relationColumnlocalColumn (on @ManyToOne and @OneToOne)
  • mappedBy → removed (replaced by relation on @InverseOf)

Query language:

  • via clause now requires a relation property — arbitrary join expressions are no longer supported