Skip to content

Commit c3e4fd7

Browse files
authored
Merge pull request #57 from CakeDC/feature/typed-association-find-by
AssociationTableMixinClassReflectionExtension must rely on generic to detect native methods
2 parents 17afd0a + 40c5cd6 commit c3e4fd7

File tree

4 files changed

+70
-7
lines changed

4 files changed

+70
-7
lines changed

src/Method/AssociationTableMixinClassReflectionExtension.php

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,15 @@ public function hasMethod(ClassReflection $classReflection, string $methodName):
5858
return true;
5959
}
6060
}
61-
6261
if (!$classReflection->is(Association::class)) {
6362
return false;
6463
}
65-
66-
// For associations, provide magic find(All)?By methods
67-
if (preg_match('/^find(All)?By/', $methodName) === 1) {
68-
return true;
64+
if (preg_match('/^find(All)?By/', $methodName) !== 1) {
65+
return false;
6966
}
67+
$classReflection = $this->getAssociationTargetClassReflection($classReflection);
7068

71-
return $this->getTableReflection()->hasMethod($methodName);
69+
return !$classReflection->hasNativeMethod($methodName);
7270
}
7371

7472
/**
@@ -120,4 +118,22 @@ public function getProperty(ClassReflection $classReflection, string $propertyNa
120118
{
121119
return $this->getTableReflection()->getNativeProperty($propertyName);
122120
}
121+
122+
/**
123+
* @param \PHPStan\Reflection\ClassReflection $classReflection
124+
* @return \PHPStan\Reflection\ClassReflection
125+
*/
126+
protected function getAssociationTargetClassReflection(ClassReflection $classReflection): ClassReflection
127+
{
128+
$subType = $classReflection->getActiveTemplateTypeMap()->getTypes()['T'] ?? null;
129+
if ($subType === null || !$subType->isObject()->yes()) {
130+
return $this->getTableReflection();
131+
}
132+
$tableClass = $subType->getObjectClassReflections()[0] ?? null;
133+
if ($tableClass !== null) {
134+
return $tableClass;
135+
}
136+
137+
return $this->getTableReflection();
138+
}
123139
}

tests/test_app/Controller/NotesController.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,33 @@ public function indexTestAssociation()
154154
$sameRole = $myUser->role === 'user';
155155
$this->set(compact('sameRole'));
156156
}
157+
158+
/**
159+
* @return void
160+
*/
161+
public function listUsers()
162+
{
163+
//UsersTable::findByNamed exists it is not a magic method
164+
$userNamed = $this->Notes->Users->findByNamed('John Doe')->id;//Users is a BelongsTo association
165+
$this->set(compact('userNamed'));
166+
$userNamedMary = $this->fetchTable('Users')->findByNamed('Mary')->id;
167+
$this->set(compact('userNamedMary'));
168+
$myUserNamed = $this->Notes->MyUsers->findByNamed('John Doe')->id;//Users is a BelongsTo association
169+
$this->set(compact('myUserNamed'));
170+
171+
//UsersTable::findByRole is a magic finder method
172+
$userList = $this->Notes->Users->findByRole('admin')->all()->toArray();
173+
$this->set(compact('userList'));
174+
$userListGuest = $this->fetchTable('Users')->findByRole('guest')->all()->toArray();
175+
$this->set(compact('userListGuest'));
176+
$myUsersList = $this->Notes->MyUsers->findByRole('admin')->all()->toArray();
177+
$this->set(compact('myUsersList'));
178+
179+
//UsersTable::findAllByFoo exists it is not a magic method
180+
$users = $this->Notes->Users->findAllByFoo(100) / 20;
181+
$myUsers = $this->Notes->MyUsers->findAllByFoo(100) / 30;
182+
$this->set(compact('users', 'myUsers'));
183+
//BelongsTo should match the correct Users table methods.
184+
$this->Notes->Users->blockOld();
185+
}
157186
}

tests/test_app/Model/Table/NotesTable.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* @method \App\Model\Entity\Note|\Cake\Datasource\EntityInterface get(mixed $primaryKey, string[]|string $finder = 'all',\Psr\SimpleCache\CacheInterface|string|null $cache = null,\Closure|string|null $cacheKey = null, mixed ...$args)
2222
* @property \App\Model\Table\VeryCustomize00009ArticlesTable&\Cake\ORM\Association\HasMany $VeryCustomize00009Articles
2323
* @property \Cake\ORM\Association\BelongsTo<\App\Model\Table\UsersTable> $Users
24-
* @property \Cake\ORM\Association\BelongsTo&\App\Model\Table\UsersTable $MyUsers
24+
* @property \Cake\ORM\Association\BelongsTo&\App\Model\Table\UsersTable $MyUsers//Don't use generic here, we need this way for testing
2525
*/
2626
class NotesTable extends Table
2727
{

tests/test_app/Model/Table/UsersTable.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,22 @@ public function blockOld(): void
4949
$articleQuery = $this->Articles->findByTitle('Test');
5050
$articleQuery->first();
5151
}
52+
53+
/**
54+
* @param string $name
55+
* @return \App\Model\Entity\User
56+
*/
57+
public function findByNamed(string $name): User
58+
{
59+
return new User(['name' => strtolower($name)]);
60+
}
61+
62+
/**
63+
* @param int $foo
64+
* @return int
65+
*/
66+
public function findAllByFoo(int $foo): int
67+
{
68+
return $foo * 200;
69+
}
5270
}

0 commit comments

Comments
 (0)