Skip to content

[4.x] TenancyUrlGenerator: override toRoute(), refactor#1439

Merged
stancl merged 10 commits into
masterfrom
override-toroute
Jun 6, 2026
Merged

[4.x] TenancyUrlGenerator: override toRoute(), refactor#1439
stancl merged 10 commits into
masterfrom
override-toroute

Conversation

@lukinovec

@lukinovec lukinovec commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

This PR adds the toRoute() method override to TenancyUrlGenerator. toRoute() now attempts to find a tenant equivalent of the passed route (= a route with the same name as the passed one, but with the tenant prefix) and generates URL for the tenant route. This behavior can be bypassed using the bypass parameter, like with the route() method override TenancyUrlGenerator had until now.

The primary reason for adding this is that Livewire v4 no longer uses the route() helper (which automatically prefixes the passed route name because of the override in TenancyUrlGenerator) in Livewire::getUpdateUri(). Now, it uses toRoute() (livewire/livewire@544aa3d#diff-e7609f8b0a60bde5a85067803d4e2f08f235c7cee9225a51ea67a85ff9a1d694R52), which didn't automatically swap the route for its 'tenant.'-prefixed equivalent in tenant context (until now). So for the Livewire integration to work with path identification, we need to override toRoute() as described.

The temporarySignedRoute() override got removed because temporarySignedRoute() calls route() under the hood, there's no need to specifically override temporarySignedRoute().

Note: Browsing old convos, it seems like the temporarySignedRoute() override was needed to make Livewire file uploads work with path identification, but it's not needed anymore. TenancyUrlGenerator had some changes since then, and now, I can't see the exact reason why we needed the override (temporarySignedRoute() uses route() under the hood, so the only thing that should really matter is overriding route()/toRoute()). It was likely a leftover from some older implementation.

The route() override got simplified. Since route() uses toRoute() under the hood, the route() override only has to have the prefixing logic. The rest is delegated to toRoute().

Note: Even though we override toRoute() now which route() uses for generating the URLs, we still need to override route() for its $this->routes->getByName($name) call to receive the prefixed name. For example, if route() wasn't overridden, and we only had one route: tenant.foo (no central foo route), and we'd call route('foo'), we'd get an exception saying that route "foo" wasn't found, even if automatic route name prefixing was enabled and toRoute() was overridden. With the route() override, route('foo') acts as if we passed 'tenant.foo' instead of 'foo'.

Comments in TenancyUrlGenerator and UrlGeneratorBootstrapper got updated to be more accurate. All intentionally affected methods are listed in TenancyUrlGenerator's docblock.

Summary by CodeRabbit

  • Improvements

    • Unified URL generation so route-name prefixing and tenant-parameter injection apply consistently across more URL entry points; bypass handling now reliably skips these behaviors.
  • Documentation

    • Clarified wording describing how tenant parameters are included in generated links.
  • Tests

    • Added tests verifying prefixed-route selection, tenant-parameter injection for unnamed routes, and bypass behavior.

Also update `route()` override since `parent::route()` calls `toRoute()` under the hood (similarly to how `parent::temporarySignedRoute()` calls `route()`)
@lukinovec lukinovec marked this pull request as ready for review March 9, 2026 12:39
@codecov

codecov Bot commented Mar 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.98%. Comparing base (ad4c924) to head (a5b3111).

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1439      +/-   ##
============================================
+ Coverage     85.96%   85.98%   +0.02%     
- Complexity     1174     1175       +1     
============================================
  Files           185      185              
  Lines          3426     3425       -1     
============================================
  Hits           2945     2945              
+ Misses          481      480       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

lukinovec and others added 3 commits March 10, 2026 12:17
The `toRoute()` and `route()` overrides cover `temporarySignedRoute())`, so we don't need to specifically override `temporarySIgnedRoute()` anymore. `route()` override got simplified since everything except for the name prefixing is delegated to the lower-level `toRoute()` method.
@coderabbitai

coderabbitai Bot commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d8da53e6-7145-420d-a93f-2b1bd76ecce3

📥 Commits

Reviewing files that changed from the base of the PR and between ac90ef0 and a5b3111.

📒 Files selected for processing (1)
  • src/Overrides/TenancyUrlGenerator.php

📝 Walkthrough

Walkthrough

Centralized route-name prefixing and tenant-parameter handling by introducing a toRoute() override and adjusting route() to normalize inputs; a docblock wording change clarifies tenant-parameter placement. Tests added to validate toRoute() prefixing and bypass/tenant-parameter behaviors. No public signatures were widened.

Changes

TenancyUrlGenerator updates

Layer / File(s) Summary
Class docblocks and flags
src/Overrides/TenancyUrlGenerator.php
Updates class-level docblock and phpdocs for bypass and tenant-parameter flags to reflect toRoute()-centric behavior.
route() normalization and toRoute() override
src/Overrides/TenancyUrlGenerator.php
route() now validates/normalizes route names (backed-enum handling) and computes an effective name. New toRoute($route, $parameters, $absolute) centralizes tenant-parameter injection, bypass removal, optional tenant-prefixed route lookup/swap, and delegates to parent::toRoute(). Removed temporarySignedRoute() override.
prepareRouteInputs() signature & logic
src/Overrides/TenancyUrlGenerator.php
prepareRouteInputs() updated to accept `string
Bootstrapper docblock
src/Bootstrappers/UrlGeneratorBootstrapper.php
Docblock reworded to clarify that the tenant parameter (from PathTenantResolver::tenantParameterName()) is passed to links produced by URL-generation methods. No executable code changed.
Tests
tests/Bootstrappers/UrlGeneratorBootstrapperTest.php
Added tests validating url()->toRoute() behavior: tenant-prefixed route selection when $prefixRouteNames = true, bypass parameter preventing prefixing, tenant query-parameter injection for unnamed routes when enabled, and bypass preventing tenant-parameter addition.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant TenancyUrlGenerator
  participant Router
  participant ParentUrlGenerator
  Caller->>TenancyUrlGenerator: call route()/toRoute($route, $parameters, $absolute)
  TenancyUrlGenerator->>Router: lookup prefixed route name (if computed)
  Router-->>TenancyUrlGenerator: return prefixed route or null
  TenancyUrlGenerator->>ParentUrlGenerator: parent::toRoute(resolvedRoute, mutatedParameters, $absolute)
  ParentUrlGenerator-->>Caller: generated URL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through names both named and bare,
I tuck the tenant token into query air,
toRoute() digs tunnels, swaps names on the way,
A small bypass gate keeps central paths at play,
I nibble bytes and leave a trail of URL care.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main change: adding a toRoute() override to TenancyUrlGenerator, matching the primary purpose of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch override-toroute

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Overrides/TenancyUrlGenerator.php`:
- Around line 141-149: The parameter mutation is skipped for unnamed Route
objects because $route->getName() gates the call to prepareRouteInputs; change
the logic in TenancyUrlGenerator::toRoute so you always call
$this->prepareRouteInputs($name, Arr::wrap($parameters)) (or call it with
null/$name when route has no name) to apply tenant-parameter injection and
bypass-parameter removal, but keep the route swap conditional: only replace
$route when $prefixedName !== $name and $this->routes->getByName($prefixedName)
returns a route; ensure prepareRouteInputs and the passTenantParameterToRoutes
behavior run even when $route->getName() is falsy before delegating to
parent::toRoute().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 17d8031d-e2be-43de-9b4c-44e8b71136d2

📥 Commits

Reviewing files that changed from the base of the PR and between 60dd522 and dc2be5b.

📒 Files selected for processing (3)
  • src/Bootstrappers/UrlGeneratorBootstrapper.php
  • src/Overrides/TenancyUrlGenerator.php
  • tests/Bootstrappers/UrlGeneratorBootstrapperTest.php

Comment thread src/Overrides/TenancyUrlGenerator.php
If an unnamed route is passed to url()->toRoute(), the generated URL will receive the tenant parameter as long as the bypass parameter wasn't passed. Also remove the bypass parameter from the generated URL.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Overrides/TenancyUrlGenerator.php`:
- Around line 128-130: The route() method currently calls prepareRouteInputs()
and discards the mutated $parameters, causing duplicate work and potential side
effects; create a small helper (e.g., normalizeRouteName or prepareRouteName) in
the TenancyUrlGenerator class that performs only the name normalization logic
extracted from prepareRouteInputs() and returns the normalized $name without
mutating $parameters, then update route() to call that helper and pass the
original $parameters into parent::route($name, $parameters, $absolute); keep
prepareRouteInputs() and toRoute() unchanged so full parameter resolution still
happens only when needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1ef2ed10-fc71-412c-80f0-c96ee9ac799c

📥 Commits

Reviewing files that changed from the base of the PR and between dc2be5b and bff74c8.

📒 Files selected for processing (2)
  • src/Overrides/TenancyUrlGenerator.php
  • tests/Bootstrappers/UrlGeneratorBootstrapperTest.php

Comment thread src/Overrides/TenancyUrlGenerator.php
@lukinovec

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@stancl stancl self-assigned this May 6, 2026
@stancl stancl requested a review from Copilot June 6, 2026 20:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the tenancy-aware URL generation behavior by overriding TenancyUrlGenerator::toRoute() so that tenant route-name prefixing and tenant-parameter injection also apply to URL generation flows that bypass the route() helper (notably Livewire v4’s update URI generation).

Changes:

  • Override TenancyUrlGenerator::toRoute() to swap a passed route for its tenant-prefixed equivalent (when applicable) and to apply tenant-parameter injection/bypass behavior.
  • Simplify the route() override to focus on ensuring the lookup name is prefixed before getByName() runs, delegating parameter handling to toRoute().
  • Add/extend tests to cover toRoute() name-prefixing, tenant-parameter injection for unnamed routes, and bypass behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/Overrides/TenancyUrlGenerator.php Adds toRoute() override and refactors route-name prefixing/parameter handling flow.
tests/Bootstrappers/UrlGeneratorBootstrapperTest.php Adds test coverage for toRoute() prefixing, unnamed-route parameter injection, and bypass behavior.
src/Bootstrappers/UrlGeneratorBootstrapper.php Updates documentation comments to reflect the broadened “affected methods” behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 128 to 131

[$name, $parameters] = $this->prepareRouteInputs($name, Arr::wrap($parameters)); // @phpstan-ignore argument.type
[$name] = $this->prepareRouteInputs(Arr::wrap($parameters), $name); // @phpstan-ignore argument.type

return parent::route($name, $parameters, $absolute);

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.

This might be a reasonable suggestion but to do the name prefixing (what we do in prepareRouteInputs) we do need the parameters array so looking at that method, the only overhead we'd avoid would be the $parameter manipulation as mentioned here:

$parameters = $this->addTenantParameter($parameters);
// ...
// Remove bypass parameter from the route parameters
unset($parameters[static::$bypassParameter]);

Which isn't nothing but probably not worth complicating the code over. I'm just noting this here to acknowledge this suggestion in case we decide to change this in the future.

Comment thread src/Overrides/TenancyUrlGenerator.php
@stancl stancl merged commit dfb0e1a into master Jun 6, 2026
14 checks passed
@stancl stancl deleted the override-toroute branch June 6, 2026 21:52
@stancl stancl changed the title [4.x] TenancyUrlGenerator: override toRoute() [4.x] TenancyUrlGenerator: override toRoute(), refactor Jun 6, 2026
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.

3 participants