Skip to content

Commit 72cff1e

Browse files
authored
Fix duplicate alias generation when joining same relationship multiple times (#214)
* Fix duplicate alias generation when joining same relationship multiple times Replace time() with uniqid() in generateAliasForRelationship() to ensure unique aliases are generated even when called multiple times within the same second. This prevents later joins from overwriting earlier ones due to identical alias names. * Fix test to be database-agnostic for MySQL compatibility Update alias count check to handle both double quotes (PostgreSQL/SQLite) and backticks (MySQL) when counting table aliases in generated SQL.
1 parent 96e1a06 commit 72cff1e

File tree

2 files changed

+72
-3
lines changed

2 files changed

+72
-3
lines changed

src/JoinsHelper.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,12 @@ public function generateAliasForRelationship(Relation $relation, string $relatio
154154
{
155155
if ($relation instanceof BelongsToMany || $relation instanceof HasManyThrough) {
156156
return [
157-
md5($relationName.'table1'.time()),
158-
md5($relationName.'table2'.time()),
157+
md5($relationName.'table1'.uniqid('', true)),
158+
md5($relationName.'table2'.uniqid('', true)),
159159
];
160160
}
161161

162-
return md5($relationName.time());
162+
return md5($relationName.uniqid('', true));
163163
}
164164

165165
/**
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace Kirschbaum\PowerJoins\Tests;
4+
5+
use Kirschbaum\PowerJoins\JoinsHelper;
6+
use Kirschbaum\PowerJoins\Tests\Models\User;
7+
8+
class DuplicateAliasGenerationTest extends TestCase
9+
{
10+
/** @test */
11+
public function test_multiple_joins_of_same_relationship_generate_unique_aliases()
12+
{
13+
$joinsHelper = JoinsHelper::make(new User());
14+
$user = new User();
15+
$relation = $user->posts();
16+
17+
// Generate multiple aliases for the same relationship quickly
18+
$alias1 = $joinsHelper->generateAliasForRelationship($relation, 'posts');
19+
$alias2 = $joinsHelper->generateAliasForRelationship($relation, 'posts');
20+
$alias3 = $joinsHelper->generateAliasForRelationship($relation, 'posts');
21+
22+
// These should all be unique, but due to time() they will likely be the same
23+
$this->assertNotEquals($alias1, $alias2, 'First and second aliases should be different');
24+
$this->assertNotEquals($alias2, $alias3, 'Second and third aliases should be different');
25+
$this->assertNotEquals($alias1, $alias3, 'First and third aliases should be different');
26+
}
27+
28+
/** @test */
29+
public function test_multiple_joins_of_same_belongs_to_many_relationship_generate_unique_aliases()
30+
{
31+
$joinsHelper = JoinsHelper::make(new User());
32+
$user = new User();
33+
$relation = $user->groups();
34+
35+
// Generate multiple aliases for the same BelongsToMany relationship quickly
36+
$alias1 = $joinsHelper->generateAliasForRelationship($relation, 'groups');
37+
$alias2 = $joinsHelper->generateAliasForRelationship($relation, 'groups');
38+
$alias3 = $joinsHelper->generateAliasForRelationship($relation, 'groups');
39+
40+
// These should all be unique arrays, but due to time() they will likely be the same
41+
$this->assertNotEquals($alias1, $alias2, 'First and second alias arrays should be different');
42+
$this->assertNotEquals($alias2, $alias3, 'Second and third alias arrays should be different');
43+
$this->assertNotEquals($alias1, $alias3, 'First and third alias arrays should be different');
44+
45+
// Also check individual elements
46+
$this->assertNotEquals($alias1[0], $alias2[0], 'First elements of alias arrays should be different');
47+
$this->assertNotEquals($alias1[1], $alias2[1], 'Second elements of alias arrays should be different');
48+
}
49+
50+
/** @test */
51+
public function test_actual_query_with_multiple_auto_generated_aliases_fails()
52+
{
53+
// This test demonstrates the real-world issue where multiple joins
54+
// with auto-generated aliases can overwrite each other
55+
56+
$query = User::query()
57+
->joinRelationshipUsingAlias('posts')
58+
->joinRelationshipUsingAlias('posts'); // Second join of same relationship
59+
60+
$sql = $query->toSql();
61+
62+
// Count how many times "posts" appears with "as" in the SQL (database-agnostic)
63+
$aliasCount = substr_count($sql, '"posts" as') + substr_count($sql, '`posts` as');
64+
65+
// We expect 2 different aliases, but due to the time() issue,
66+
// we might get the same alias twice, resulting in only 1 unique join
67+
$this->assertEquals(2, $aliasCount, 'Should have 2 different aliases for posts table, but got: '.$sql);
68+
}
69+
}

0 commit comments

Comments
 (0)