Skip to content

Commit 54e1846

Browse files
committed
Refactor tests for extended relationships: enhance database operations, add null handling, and improve eager loading
- Updated `BelongsToManyKeysTest` to work with actual database operations and multiple keys, ensuring proper matching of models with user associations. - Introduced `DebugQueryTest` to validate direct queries against the database. - Refined `HasManyArrayColumnTest` to utilize real database interactions, ensuring accurate handling of eager loading and null values. - Enhanced `HasManyKeysTest` to support multiple foreign keys and validate relationships through database records. - Improved `TestCase` setup to create necessary database schemas for testing, ensuring a consistent environment for all tests.
1 parent f8a5a7f commit 54e1846

11 files changed

+459
-622
lines changed

src/Relations/BelongsToArrayColumn.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ public function match(array $models, Collection $results, $relation): array
6767
$id = $model->getAttribute($this->foreignKey);
6868
$collection = collect();
6969
foreach ($results as $data) {
70-
if (in_array($id, $data->{$owner}, true)) {
70+
$ownerValue = $data->{$owner};
71+
if (is_array($ownerValue) && in_array($id, $ownerValue, true)) {
7172
$collection->push($data);
7273
}
7374
}

src/Relations/HasManyKeys.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,17 @@ public function addConstraints(): void
6565
public function addEagerConstraints(array $models): void
6666
{
6767
$foreignKeys = $this->foreignKeys;
68-
$this->query->where(function ($query) use ($foreignKeys, $models): void {
68+
$keys = $this->getKeys($models, $this->localKey);
69+
70+
$this->query->where(function ($query) use ($foreignKeys, $keys): void {
71+
$first = true;
6972
foreach ($foreignKeys as $foreignKey) {
70-
$query->orWhereIn($foreignKey, $this->getKeys($models, $this->localKey));
73+
if ($first) {
74+
$query->whereIn($foreignKey, $keys);
75+
$first = false;
76+
} else {
77+
$query->orWhereIn($foreignKey, $keys);
78+
}
7179
}
7280
});
7381
}
@@ -102,6 +110,8 @@ public function match(array $models, Collection $results, $relation): array
102110
foreach ($this->foreignKeys as $foreignKey) {
103111
if (isset($dictionary[$foreignKey][$key])) {
104112
$desireRelations->{$this->relations[$foreignKey]} = $dictionary[$foreignKey][$key];
113+
} else {
114+
$desireRelations->{$this->relations[$foreignKey]} = $this->related->newCollection();
105115
}
106116
}
107117
$model->setRelation($relation, $desireRelations);
@@ -119,7 +129,10 @@ public function buildDictionary(Collection $models): array
119129
$dictionary = [];
120130
foreach ($models as $model) {
121131
foreach ($this->foreignKeys as $foreignKey) {
122-
$dictionary[$foreignKey][$model->{$foreignKey}] = $model;
132+
if (! isset($dictionary[$foreignKey][$model->{$foreignKey}])) {
133+
$dictionary[$foreignKey][$model->{$foreignKey}] = $this->related->newCollection();
134+
}
135+
$dictionary[$foreignKey][$model->{$foreignKey}]->push($model);
123136
}
124137
}
125138

tests/HasExtendedRelationshipsTest.php

Lines changed: 55 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -8,195 +8,67 @@
88
use Mrpunyapal\LaravelExtendedRelationships\Relations\HasManyKeys;
99
use Mrpunyapal\LaravelExtendedRelationships\Tests\Models\Company;
1010
use Mrpunyapal\LaravelExtendedRelationships\Tests\Models\Post;
11-
use Mrpunyapal\LaravelExtendedRelationships\Tests\Models\Tag;
1211
use Mrpunyapal\LaravelExtendedRelationships\Tests\Models\User;
1312

14-
it('can create belongsToManyKeys relationship', function () {
13+
it('can create all relationship types through trait methods', function () {
1514
$post = new Post;
16-
17-
$relation = $post->belongsToManyKeys(
18-
User::class,
19-
'id',
20-
['created_by' => 'creator', 'updated_by' => 'updater']
21-
);
22-
23-
expect($relation)->toBeInstanceOf(BelongsToManyKeys::class);
24-
});
25-
26-
it('can create hasManyKeys relationship', function () {
27-
$user = new User;
28-
29-
$relation = $user->hasManyKeys(
30-
Post::class,
31-
['created_by' => 'created', 'updated_by' => 'updated'],
32-
'id'
33-
);
34-
35-
expect($relation)->toBeInstanceOf(HasManyKeys::class);
36-
});
37-
38-
it('can create hasManyArrayColumn relationship', function () {
3915
$user = new User;
40-
41-
$relation = $user->hasManyArrayColumn(
42-
Company::class,
43-
'id',
44-
'companies'
45-
);
46-
47-
expect($relation)->toBeInstanceOf(HasManyArrayColumn::class);
48-
});
49-
50-
it('can create belongsToArrayColumn relationship', function () {
51-
$company = new Company;
52-
53-
$relation = $company->belongsToArrayColumn(
54-
User::class,
55-
'id',
56-
'user_ids'
57-
);
58-
59-
expect($relation)->toBeInstanceOf(BelongsToArrayColumn::class);
60-
});
61-
62-
it('can create belongsToArrayColumn relationship with string flag', function () {
6316
$company = new Company;
6417

65-
$relation = $company->belongsToArrayColumn(
66-
User::class,
67-
'id',
68-
'user_ids',
69-
true
70-
);
71-
72-
expect($relation)->toBeInstanceOf(BelongsToArrayColumn::class);
18+
// Test that all relationship methods return correct instances
19+
expect($post->belongsToManyKeys(User::class, 'id', ['created_by' => 'creator']))->toBeInstanceOf(BelongsToManyKeys::class)
20+
->and($user->hasManyKeys(Post::class, ['created_by' => 'created'], 'id'))->toBeInstanceOf(HasManyKeys::class)
21+
->and($user->hasManyArrayColumn(Company::class, 'id', 'companies'))->toBeInstanceOf(HasManyArrayColumn::class)
22+
->and($company->belongsToArrayColumn(User::class, 'id', 'user_ids'))->toBeInstanceOf(BelongsToArrayColumn::class)
23+
->and($company->belongsToArrayColumn(User::class, 'id', 'user_ids', true))->toBeInstanceOf(BelongsToArrayColumn::class);
7324
});
7425

75-
it('creates related query correctly', function () {
76-
$user = new User;
77-
78-
// Test that relatedNewQuery creates a proper query builder
79-
$reflection = new ReflectionClass($user);
80-
$method = $reflection->getMethod('relatedNewQuery');
81-
$method->setAccessible(true);
82-
83-
$query = $method->invoke($user, User::class);
84-
85-
expect($query)->toBeInstanceOf(\Illuminate\Database\Eloquent\Builder::class);
86-
});
87-
88-
it('can define auditors relationship using belongsToManyKeys', function () {
89-
$post = new class extends Post
90-
{
91-
public function auditors()
92-
{
93-
return $this->belongsToManyKeys(
94-
User::class,
95-
'id',
96-
[
97-
'created_by' => 'creator',
98-
'updated_by' => 'updater',
99-
'deleted_by' => 'deleter',
100-
]
101-
);
102-
}
103-
};
104-
105-
$relation = $post->auditors();
106-
107-
expect($relation)->toBeInstanceOf(BelongsToManyKeys::class);
108-
});
109-
110-
it('can define audited relationship using hasManyKeys', function () {
111-
$user = new class extends User
112-
{
113-
public function audited()
114-
{
115-
return $this->hasManyKeys(
116-
Post::class,
117-
[
118-
'created_by' => 'created',
119-
'updated_by' => 'updated',
120-
'deleted_by' => 'deleted',
121-
],
122-
'id'
123-
);
124-
}
125-
};
126-
127-
$relation = $user->audited();
128-
129-
expect($relation)->toBeInstanceOf(HasManyKeys::class);
130-
});
131-
132-
it('can define companies relationship using hasManyArrayColumn', function () {
133-
$user = new class extends User
134-
{
135-
public function myCompanies()
136-
{
137-
return $this->hasManyArrayColumn(
138-
Company::class,
139-
'id',
140-
'companies'
141-
);
142-
}
143-
};
144-
145-
$relation = $user->myCompanies();
146-
147-
expect($relation)->toBeInstanceOf(HasManyArrayColumn::class);
148-
});
149-
150-
it('can define founders relationship using belongsToArrayColumn', function () {
151-
$company = new class extends Company
152-
{
153-
public function founders()
154-
{
155-
return $this->belongsToArrayColumn(
156-
User::class,
157-
'id',
158-
'founder_ids'
159-
);
160-
}
161-
};
162-
163-
$relation = $company->founders();
164-
165-
expect($relation)->toBeInstanceOf(BelongsToArrayColumn::class);
166-
});
167-
168-
it('can define tags relationship using hasManyArrayColumn', function () {
169-
$post = new class extends Post
170-
{
171-
public function tags()
172-
{
173-
return $this->hasManyArrayColumn(
174-
Tag::class,
175-
'id',
176-
'tag_ids'
177-
);
178-
}
179-
};
180-
181-
$relation = $post->tags();
182-
183-
expect($relation)->toBeInstanceOf(HasManyArrayColumn::class);
184-
});
185-
186-
it('can define posts relationship using belongsToArrayColumn', function () {
187-
$tag = new class extends Tag
188-
{
189-
public function posts()
190-
{
191-
return $this->belongsToArrayColumn(
192-
Post::class,
193-
'id',
194-
'post_ids'
195-
);
196-
}
197-
};
198-
199-
$relation = $tag->posts();
200-
201-
expect($relation)->toBeInstanceOf(BelongsToArrayColumn::class);
26+
it('integrates all relationship types with database operations', function () {
27+
// Create base entities
28+
$author = User::create(['id' => 1000, 'name' => 'Jane Author', 'email' => 'jane@example.com', 'companies' => [1, 2], 'company_ids' => [1, 2]]);
29+
$editor = User::create(['id' => 2000, 'name' => 'John Editor', 'email' => 'john@example.com', 'companies' => [2, 3], 'company_ids' => [2, 3]]);
30+
31+
Company::create(['id' => 1, 'name' => 'Tech Startup']);
32+
Company::create(['id' => 2, 'name' => 'Design Agency']);
33+
Company::create(['id' => 3, 'name' => 'Consulting Firm']);
34+
35+
$post = Post::create([
36+
'id' => 5000,
37+
'title' => 'Integration Test Post',
38+
'content' => 'Testing all relationships',
39+
'created_by' => 1000,
40+
'updated_by' => 2000,
41+
]);
42+
43+
// Test BelongsToManyKeys - post to users
44+
$postAuditors = $post->belongsToManyKeys(User::class, 'id', ['created_by' => 'creator', 'updated_by' => 'updater']);
45+
$users = User::whereIn('id', [1000, 2000])->get();
46+
$matched = $postAuditors->match([$post], $users, 'auditors');
47+
48+
expect($matched[0]->auditors->creator->name)->toBe('Jane Author')
49+
->and($matched[0]->auditors->updater->name)->toBe('John Editor');
50+
51+
// Test HasManyKeys - user to posts
52+
$userPosts = $author->hasManyKeys(Post::class, ['created_by' => 'created', 'updated_by' => 'updated'], 'id');
53+
$posts = Post::where('id', 5000)->get();
54+
$matchedUsers = $userPosts->match([$author, $editor], $posts, 'audited');
55+
56+
expect($matchedUsers[0]->audited->created)->toHaveCount(1)
57+
->and($matchedUsers[1]->audited->updated)->toHaveCount(1);
58+
59+
// Test HasManyArrayColumn - user to companies
60+
$userCompanies = $author->hasManyArrayColumn(Company::class, 'id', 'companies');
61+
$companies = Company::whereIn('id', [1, 2, 3])->get();
62+
$matchedForCompanies = $userCompanies->matchMany([$author, $editor], $companies, 'workplaces');
63+
64+
expect($matchedForCompanies[0]->workplaces)->toHaveCount(2)
65+
->and($matchedForCompanies[1]->workplaces)->toHaveCount(2);
66+
67+
// Test BelongsToArrayColumn - company to users (through company_ids)
68+
$company = Company::find(2);
69+
$companyEmployees = $company->belongsToArrayColumn(User::class, 'id', 'company_ids');
70+
$allUsers = User::whereIn('id', [1000, 2000])->get();
71+
$matchedEmployees = $companyEmployees->match([$company], $allUsers, 'employees');
72+
73+
expect($matchedEmployees[0]->employees)->toHaveCount(2); // Both users reference company 2
20274
});

tests/Models/Company.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,9 @@ class Company extends Model
2424
];
2525

2626
public $timestamps = false;
27+
28+
public function employees()
29+
{
30+
return $this->belongsToArrayColumn(User::class, 'id', 'company_ids');
31+
}
2732
}

tests/Models/User.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class User extends Model
1717
'email',
1818
'companies',
1919
'user_ids',
20+
'company_ids',
2021
'created_by',
2122
'updated_by',
2223
'deleted_by',
@@ -25,6 +26,7 @@ class User extends Model
2526
protected $casts = [
2627
'companies' => 'array',
2728
'user_ids' => 'array',
29+
'company_ids' => 'array',
2830
];
2931

3032
public $timestamps = false;

0 commit comments

Comments
 (0)