Skip to content

[13.x] Fix Relation::getMorphAlias() for zero aliases#60818

Closed
Amirhf1 wants to merge 1 commit into
laravel:13.xfrom
Amirhf1:fix-zero-morph-alias
Closed

[13.x] Fix Relation::getMorphAlias() for zero aliases#60818
Amirhf1 wants to merge 1 commit into
laravel:13.xfrom
Amirhf1:fix-zero-morph-alias

Conversation

@Amirhf1

@Amirhf1 Amirhf1 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

The Problem

When looking up a morph map alias via Relation::getMorphAlias(), the method uses a shorthand ternary operator (?:) on the result of array_search():

return array_search($className, static::$morphMap) ?:$className;

If a model is mapped to the integer 0 (which is a common practice when using legacy databases or ultra-optimized numeric discriminators), array_search() correctly finds and returns 0.

However, because PHP evaluates 0 as falsey, the shorthand ternary discards the alias and falls back to returning the full class name instead.

The Solution
This PR replaces the loose ternary check with an explicit type check (!== false) against the result of array_search(). This ensures that 0 is properly preserved and returned as a valid alias, making its behavior consistent with all other numeric/integer aliases (like 1 or 10).

@shaedrich

Copy link
Copy Markdown
Contributor

That might be in the database but how would you map a substitute for null that 0 seems to be to literally anything?

@Amirhf1

Amirhf1 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

That might be in the database but how would you map a substitute for null that 0 seems to be to literally anything?

Just to clarify, 0 is not being used as a substitute for null here—an absent/nullable polymorphic relation would still use an actual database null. Instead, 0 is used as an explicit, intentional discriminator stored in the morph type column.

For instance, in legacy schemas or highly optimized databases, developers often map types using non-sequential integers like this:

Relation::morphMap([
    0 => Admin::class,
    10 => User::class,
], false);

(Note: We use non-sequential keys like 0 and 10 here because sequential keys like 0 and 1 are interpreted by Laravel as a simple list of model classes rather than custom aliases).

The interesting part is that the forward lookup already works perfectly:

Relation::getMorphedModel('0'); // Returns: Admin::class

However, the reverse lookup currently fails. Calling Relation::getMorphAlias(Admin::class) incorrectly returns the full Admin::class string instead of 0 simply because the shorthand ternary (?:) treats the successful 0 index returned by array_search() as falsey.

Since integer aliases are fully supported by the morph map API and getMorphAlias() is already documented to return string|int, this change simply makes the 0 alias consistent with both other integer aliases (like 10) and the existing forward lookup.

@shaedrich

Copy link
Copy Markdown
Contributor

No offense, but this sounds absolutely wild. Not sure if Laravel should encourage this.

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