Skip to content
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

Problem with relations #25

Open
b1rdex opened this issue Dec 17, 2019 · 7 comments · May be fixed by #54
Open

Problem with relations #25

b1rdex opened this issue Dec 17, 2019 · 7 comments · May be fixed by #54

Comments

@b1rdex
Copy link

b1rdex commented Dec 17, 2019

Code in the controller:

        /**
         * @var Transfer|null $transfer
         */
        $transfer = Transfer::find()->one();
        if (!$transfer) {
            return;
        }
        $paymentsTransfers = $transfer->getPaymentTransfer()->all();

Model:

class Transfer extends \yii\db\ActiveRecord
{
    /**
     * @return \yii\db\ActiveQuery
     */
    public function getPaymentTransfer()
    {
        return $this->hasMany(PaymentTransfer::class, ['transfer_id' => 'id']);
    }
}

getPaymentTransfer() method return type provided in PhpDoc or using native return type set to ActiveQuery leads to extension exception: Internal error: Unexpected type PHPStan\Type\ObjectType during method call all at line 142 (line 142 is $transfer->getPaymentTransfer()->all() call). That error comes from \Proget\PHPStan\Yii2\Type\ActiveQueryDynamicMethodReturnTypeExtension::getTypeFromMethodCall. Here is what's in the $calledOnType:

object(PHPStan\Type\ObjectType)#9382 (3) {
  ["className":"PHPStan\Type\ObjectType":private]=>
  string(18) "yii\db\ActiveQuery"
  ["subtractedType":"PHPStan\Type\ObjectType":private]=>
  NULL
  ["genericObjectType":"PHPStan\Type\ObjectType":private]=>
  NULL
}

If I remove return type from getPaymentTransfer() everything works fine. But that looks odd and that's easy to break if someone adds return type.

@flaviovs
Copy link

flaviovs commented Apr 6, 2020

This is not limited to relations. I am facing this issue with simple methods returning yii\db\ActiveQuery, such as:

public static function findBySomething(string $something): \yii\db\ActiveQuery
{
    return self::find()
        ->where(['something' => $something]);
}

@EtienneBruines
Copy link
Contributor

I've currently got a working version for

MyModel::findBySql("")->all();

and anything that returns an ActiveQuery.

However, saving it to a variable is still a work-in-progress (because I do not know how to access the MyModel part here):

$query = MyModel::findBySql("");
$query->all();

If anyone has any knowledge, there's an open question at phpstan.


Modified code - click to expand
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
    {
        $methodName = $methodReflection->getName();
        $calledOnType = $scope->getType($methodCall->var);
        
        if (!$calledOnType instanceof ActiveQueryObjectType) {
            if (!($calledOnType instanceof ObjectType) || $calledOnType->getClassName() !== ActiveQuery::class) {
                throw new ShouldNotHappenException(sprintf('Unexpected type %s during method call %s at line %d', \get_class($calledOnType), $methodReflection->getName(), $methodCall->getLine()));
            }

            $var = $methodCall->var;
            if ($var instanceof StaticCall) {
                /**
                 * @example
                 * MyModel::findBySql("")->all();
                 */
                $calledOnType = new ActiveQueryObjectType($var->class->toString(), $methodName !== 'one');
            } else if ($var instanceof Variable) {
                /**
                 * @example :
                 * $query = MyModel::findBySql("");
                 * $query->all();
                 */
                // TODO $calledOnType = new  ActiveQueryObjectType('??????????', $methodName === 'one');
            } else {
                throw new ShouldNotHappenException(sprintf('Unable to find ActiveRecord type for %s during method call %s at line %d', \get_class($calledOnType), $methodReflection->getName(), $methodCall->getLine()));
            }
        }
        
        if ($methodName === 'asArray') {
            $argType = isset($methodCall->args[0]) ? $scope->getType($methodCall->args[0]->value) : new ConstantBooleanType(true);
            if (!$argType instanceof ConstantBooleanType) {
                throw new ShouldNotHappenException(sprintf('Invalid argument provided to asArray method at line %d', $methodCall->getLine()));
            }

            return new ActiveQueryObjectType($calledOnType->getModelClass(), $argType->getValue());
        }

        if (!\in_array($methodName, ['one', 'all'], true)) {
            return new ActiveQueryObjectType($calledOnType->getModelClass(), $calledOnType->isAsArray());
        }

        if ($methodName === 'one') {
            return TypeCombinator::union(
                new NullType(),
                $calledOnType->isAsArray() ? new ArrayType(new StringType(), new MixedType()) : new ObjectType($calledOnType->getModelClass())
            );
        }

        return new ArrayType(
            new IntegerType(),
            $calledOnType->isAsArray() ? new ArrayType(new StringType(), new MixedType()) : new ObjectType($calledOnType->getModelClass())
        );
    }

@XMarat
Copy link

XMarat commented Dec 2, 2021

I solved like this

/** @var null $query */
$query = Transfer::find()
/** @var Transfer $transfer */
$transfer = $query->one(); // @phpstan-ignore-line

@XMarat
Copy link

XMarat commented Dec 10, 2021

i fix this #48

@porozhnyy
Copy link

@marmichalski Hey! Are you repeating this error? A simple example that shows the current problem and is very annoying:

<?php

namespace myNameSpace;

use yii\db\ActiveQuery;

class MyQuery extends ActiveQuery
{
    public function test(array $ids): ActiveQuery
    {
        return $this->andWhere(['not in', 'id', $ids]);
    }
}
 ------ ---------------------------------------------------------------------------------------------- 
  Line   MyQuery.php                                                                      
 ------ ---------------------------------------------------------------------------------------------- 
         Internal error: Unexpected type PHPStan\Type\ThisType during method call andWhere at line 19  

I tried changing the validation myself, but my current knowledge is a bit lacking. I will try to figure it out further, but maybe you have a solution to this problem?

@optmsp
Copy link

optmsp commented Sep 22, 2022

I am trying this out and also getting this error a lot:

Internal error: Unexpected type PHPStan\Type\ThisType during method call andWhere...

Anybody have a solution?

@lisotton
Copy link

I solved like this

/** @var null $query */
$query = Transfer::find()
/** @var Transfer $transfer */
$transfer = $query->one(); // @phpstan-ignore-line

No, you did not solve, you put the issue under the carpet.

@lisotton lisotton linked a pull request Jun 14, 2023 that will close this issue
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 a pull request may close this issue.

7 participants