Skip to content

Conversation

@peterfox
Copy link
Collaborator

@peterfox peterfox commented Dec 3, 2025

This adds:

  • A way to analyse if a method on a model is in fact a Query Scope method used in Eloquent queries.
  • A way to analyse if a Eloquent query builder is for a specified model.
  • A way to analyse if a method on a model is a relation and what Related and Parent models are used in the relation.
  • Covers tests for the new features.

Improves:

  • Some PHPDocs
  • What exceptions are thrown
  • Namespace issues in some tests
  • Allows strings or ObjectTypes to be passed to a number of previously made analyser methods.

@peterfox peterfox self-assigned this Dec 3, 2025
@peterfox peterfox added enhancement New feature or request refactor Pull Request is for the purpose of improvement but not necessarily a change in functionality labels Dec 3, 2025
@peterfox peterfox requested a review from calebdw December 3, 2025 16:59
@peterfox
Copy link
Collaborator Author

peterfox commented Dec 3, 2025

@calebdw I'll leave this PR here. I've added some relation analysis methods but I don't want to go overboard with creating lots of cool tooling without them being used more first.

$extendedPropertyReflection = $objectType->getInstanceProperty('model', $scope);
$modelType = $extendedPropertyReflection->getReadableType();

if ($modelType->isSuperTypeOf(self::modelType())->no()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your super type checks are still backwards (there's a lot of them that need to be updated)---the super type needs to be outside the function call not inside (e.g., $user->isSuperTypeOf($model) will fail because User is not a super type of Model; it should be $model->isSuperTypeOf($user))

Suggested change
if ($modelType->isSuperTypeOf(self::modelType())->no()) {
if (self::modelType()->isSuperTypeOf($modelType)->no()) {

$extendedMethodReflection = $classReflection->getMethod($relationName, $scope);

foreach ($extendedMethodReflection->getVariants() as $extendedParametersAcceptor) {
if ($extendedParametersAcceptor->getReturnType()->isSuperTypeOf(self::relationType())->yes()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ($extendedParametersAcceptor->getReturnType()->isSuperTypeOf(self::relationType())->yes()) {
if (self::relationType()->isSuperTypeOf($extendedParametersAcceptor->getReturnType())->yes()) {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure for this one? When I change it, the tests break for scenario it_can_determine_if_the_model_has_relationship_method at line tests/NodeAnalyzer/ModelAnalyzerTest.php:117

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I'm sure, the problem is that the relationship needs the return type:

    public function relationship(): HasMany

Without it, phpstan is resolving the return type as mixed and so your test is a false positive because you're asking if mixed is a supertype of Relation which is true

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would PHPStan now infer the correct type purely from the fact it always returns a HasMany object anyways?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well you're right although I'm a little confused still because I assumed it would infer the type from the PHPDocs and the return of the method but I guess not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHPStan doesn't descend into method and functions---it stops at the boundary. When inside a method phpstan checks that you return what you say you return, and when outside a method phpstan checks to make sure the arguments you pass are compatible

https://phpstan.org/user-guide/troubleshooting-types#what-phpstan-doesn%E2%80%99t-do

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah okay. That makes sense. Thank you for your patience with me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries! It's a common misconception, but it helps to better understand how phpstan works once you grasp it 😃

@peterfox peterfox requested a review from calebdw December 5, 2025 23:57
Copy link
Contributor

@calebdw calebdw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just a couple of more comments

@peterfox peterfox merged commit 12b1574 into main Dec 6, 2025
5 checks passed
@peterfox peterfox deleted the feature/analyse-models-builders-further branch December 6, 2025 01:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request refactor Pull Request is for the purpose of improvement but not necessarily a change in functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants