Skip to content

feat(laravel): Support composite identifiers/keys within Link #7342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

jonerickson
Copy link
Contributor

Q A
Branch? 4.1
Tickets
License MIT
Doc PR

Problem Statement

In PR #7231, we introduced link handling using native Eloquent relationships, which significantly improved support for Laravel's dynamic relationships. However, a limitation emerged when working with composite keys and alternative identifiers.

Current Issue: While composite keys aren't natively supported in Laravel ([reference](#7231)), this shouldn't prevent us from using alternative identifiers for entity lookup, particularly client-facing identifiers.

Use Case Example

Consider a common scenario where:

  • Database: Uses native bigint primary IDs for internal model relationships
  • Client-facing: Uses a reference_id column containing UUIDs for all public lookups/IRIs
  • Problem: The existing implementation never fetches the related instance using the alternate reference ID, so we can't determine which base model to use before performing the relation query

Solution

This PR introduces intelligent identifier resolution that checks whether we're using the model's primary key or an alternative identifier.

Code Changes

Before (Existing Implementation):

$relatedInstance = $this->application->make($link->getFromClass());
$relatedInstance->setAttribute($relatedInstance->getKeyName(), $identifier);
$relatedInstance->exists = true;

After (New Implementation):

$relatedInstance = $this->application->make($link->getFromClass());
$identifierField = $link->getIdentifiers()[0];

if ($identifierField !== $relatedInstance->getKeyName()) {
    // Alternative identifier: query the database to find the model
    $relatedInstance = $relatedInstance
        ->newQuery()
        ->where($identifierField, $identifier)
        ->first();
} else {
    // Primary key: use existing fast path
    $relatedInstance->setAttribute($identifierField, $identifier);
    $relatedInstance->exists = true;
}

How It Works

  1. Primary Key Path: If the identifier field matches the model's primary key (getKeyName()), we maintain the existing behavior for optimal performance
  2. Alternative Identifier Path: If using a different field (like reference_id), we query the database to locate the correct model instance
  3. Laravel ORM Integration: Once we have the correct model instance with its primary key populated, Laravel's ORM can properly form relationship queries using its standard mechanisms

Benefits

  • Flexibility: Enables use of client-facing UUIDs, slugs, or other alternative identifiers
  • Performance: Maintains fast path for primary key lookups
  • Compatibility: Zero breaking changes - existing code continues to work unchanged
  • Laravel Integration: Leverages Laravel's native relationship handling once the correct model is identified

Backward Compatibility

Fully backward compatible - no breaking changes
Existing functionality preserved - primary key lookups use the same fast path
API unchanged - no modifications to public interfaces

This enhancement allows for more flexible identifier strategies while maintaining full compatibility with existing implementations.

@soyuka soyuka merged commit 2d501b3 into api-platform:4.1 Aug 18, 2025
109 of 111 checks passed
@soyuka
Copy link
Member

soyuka commented Aug 18, 2025

awesome thnaks @jonerickson

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