Skip to content

inverse-relations and bugs-fixing#111

Open
siggi-k wants to merge 5 commits into
masterfrom
inverse-relations
Open

inverse-relations and bugs-fixing#111
siggi-k wants to merge 5 commits into
masterfrom
inverse-relations

Conversation

@siggi-k
Copy link
Copy Markdown

@siggi-k siggi-k commented May 19, 2026

Fixes and improvements to inverse relation generation in dbmodel.php template

1. Redundant filter logic removed

The method loop was manually rebuilding $coveredClasses from relations, many2many
and nonDbRelations — the same logic that $inverseRelations at the top of the template
already performs. The duplicate code and its variables were removed.

The filter behind $inverseRelations also serves an important purpose beyond deduplication:
if the auto-generated inverse relation for a class would be wrong (e.g. getBookings() instead
of getBooking()), you can declare the relation manually in the YAML spec using x-db-type: false.
This makes it a nonDbRelation, which adds the class to $allCoveredClasses and suppresses
the incorrect auto-generated inverse entirely. Instead, an abstract method is generated in the
base class, which the child class then implements correctly.

Example: Booking has a unique FK bank_account_transaction_id. Without a manual declaration,
the generator would produce getBankAccountTransactions() (hasMany) on Booking — which is wrong.
By declaring booking: x-db-type: false in the YAML, the wrong inverse is suppressed and
getBooking() (hasOne) is implemented manually in the child class instead.

2. Broken disambiguation removed ($i, $number, $usedRelationNames)

The old code tried to resolve duplicate method names by appending a number (e.g. getOrders2()).
The counter was incremented on every loop iteration including skipped ones, making the suffix
non-deterministic. This whole mechanism is made obsolete by the new naming approach in point 3.

3. Inverse relation method names now derived from the FK column name

Previously the method name was based on the related class name, which caused naming conflicts
when one model had multiple FK columns pointing to the same target.

Now the FK column name is used as the basis:

  • strip trailing _id
  • strip trailing _<modelName> (the target model's name)
  • prepend the remaining prefix to the pluralized class name

Examples: model Order has three FK columns pointing to Lead:

FK column (on Order) generated on Order generated on Lead (inverse)
lead_id getLead() getOrders()
customer_lead_id getCustomerLead() getCustomerOrders()
billing_lead_id getBillingLead() getBillingOrders()

Logic for the inverse name (target model = Lead, snake = lead):

  1. Take the FK column name: customer_lead_id
  2. Strip trailing _id: → customer_lead
  3. Strip trailing _lead: → customer
  4. Prepend prefix to pluralized class name: Customer + OrdersgetCustomerOrders()
    If no prefix remains (plain lead_idlead → ``): just getOrders()

FK column names within a model are unique by definition (DB constraint),
so naming conflicts can no longer occur.

4. Missing @property annotations added

The PHPDoc block at the top of the generated class was missing @property entries
for inverse relations. IDE type inference for these relations was broken.

5. Bug fix: skip models with x-table: false

Models declared with x-table: false have no real database table and no actual FK columns.
The filter for $inverseRelations now excludes them via $r->getTableName() !== ''.

6. Bug fixes and improvements in fakeForArray and fakeForObject (FakerStubResolver.php)

Bug fixes:

  • fakeForArray: an array property with no items definition returned $faker->words() — now returns []
  • fakeForObject: an object property with no properties definition returned $faker->words() — now returns []
  • fakeForArray with object items: when fakeForObject returned [], it was still wrapped in array_map() via wrapInArray(), producing broken faker code — now [] is returned directly

Improvements:

  • fakeForObject has a new $depth parameter enabling correct indentation for nested objects in the generated faker output
  • Replaced $this->{__FUNCTION__}($prop) with the explicit $this->fakeForObject($prop, $depth + 1)

@siggi-k siggi-k requested a review from cebe May 19, 2026 14:14
@siggi-k siggi-k self-assigned this May 19, 2026
@siggi-k siggi-k force-pushed the inverse-relations branch from 139ffcd to 479d22d Compare May 21, 2026 13:55
Comment thread src/generator/default/dbmodel.php Outdated
{
if (!$items->properties) {
return $this->arbitraryArray();
return '[]';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

empty object should be {}

Suggested change
return '[]';
return '{}';

Co-authored-by: Carsten Brandt <mail@cebe.cc>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants