phpstan-doctrine icon indicating copy to clipboard operation
phpstan-doctrine copied to clipboard

Call to an undefined method Sylius\Component\Core\Repository\*::findOneBy*() and other magic methods

Open vasilvestre opened this issue 1 year ago • 4 comments

On any released version of Sylius-Standard, I couldn't get phpstan-doctrine to work.

Full error :

Call to an undefined method Sylius\Component\Core\Repository\OrderRepositoryInterface::findOneByThing().  

Here's a minimal reproducer :

<?php

namespace App\Controller;

use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class GrumpyGnomeController extends AbstractController
{
    public function __construct(
        private readonly OrderRepositoryInterface $orderRepository,
    )
    {
    }

    #[Route('/grumpy/gnome', name: 'app_grumpy_gnome')]
    public function index(): Response
    {
        $a = $this->orderRepository->findOneByThing();
        return $this->render('grumpy_gnome/index.html.twig', [
            'controller_name' => 'GrumpyGnomeController',
        ]);
    }
}


use Sylius\Component\Order\Repository\OrderRepositoryInterface as BaseOrderRepositoryInterface;
/**
 * @template T of OrderInterface
 *
 * @extends BaseOrderRepositoryInterface<T>
 */
interface OrderRepositoryInterface extends BaseOrderRepositoryInterface


use Sylius\Component\Resource\Repository\RepositoryInterface;
/**
 * @template T of OrderInterface
 *
 * @extends RepositoryInterface<T>
 */
interface OrderRepositoryInterface extends RepositoryInterface


use Doctrine\Persistence\ObjectRepository;
/**
 * @template T of ResourceInterface
 *
 * @extends ObjectRepository<T>
 */
interface RepositoryInterface extends ObjectRepository

/**
 * Contract for a Doctrine persistence layer ObjectRepository class to implement.
 *
 * @template-covariant T of object
 */
interface ObjectRepository

phpstan.neon

includes:
    - vendor/phpstan/phpstan-doctrine/extension.neon

parameters:
    level: 4
    paths:
        - src
    doctrine:
        ormRepositoryClass: Sylius\Component\Resource\Repository\RepositoryInterface
        objectManagerLoader: tests/object-manager.php

I've configured an object-manager.php test just in case and it doesn't improve the detection.

Sylius do not use ServerEntityManager

vasilvestre avatar Jun 17 '24 12:06 vasilvestre

You can check this by running PHPStan with Xdebug and seeing where it ends in EntityRepositoryClassReflectionExtension but in my opinion this can only work if OrderInterface is an entity and has a field named $thing. Which it probably isn't because interfaces can't declare properties.

ondrejmirtes avatar Jun 17 '24 13:06 ondrejmirtes

foreach ($entityClassNames as $entityClassName) {

In this line, entityClassNames is empty.

Here's the value of $templateTypeMap :

PHPStan\Type\Generic\TemplateTypeMap {#22787                                                                              
-types: array:1 [
"TEntityClass" => PHPStan\Type\ObjectWithoutClassType {#13595                                                         
-subtractedType: null                                                                                               
}                                                                                                                     
]                                                                                                                       
-lowerBoundTypes: []
-resolvedToBounds: null                                                                                                 
}

$entityClassType :

PHPStan\Type\ObjectWithoutClassType {#13595                                                                               
	-subtractedType: null                                                                                                   
 }     

$entityClassNames is an empty array and field name is fine. (I've changed to "name" because name exists on the order entity).

I've changed the repository to use my entity instead of the interface, the error and data previously shared is the same.

/**
 * @template T of Order
 *
 * @extends BaseOrderRepositoryInterface<T>
 */
interface OrderRepositoryInterface extends BaseOrderRepositoryInterface

vasilvestre avatar Jun 19 '24 08:06 vasilvestre

I got it to work by modifying these.

Whatever I change in phpdoc of Sylius\Component\Core\Repository\OrderRepositoryInterface has no effect.

/**
 * @template T of OrderInterface
 *
 * @extends BaseOrderRepositoryInterface<T>
 */
interface OrderRepositoryInterface extends BaseOrderRepositoryInterface

Modifying type in @template had no effect Sylius\Component\Order\Repository\OrderRepositoryInterface

/**
 * @template T of Order
 *
 * @extends RepositoryInterface<T>
 */
interface OrderRepositoryInterface extends RepositoryInterface

Dropping @template and using my own entity works here Sylius\Component\Order\Repository\OrderRepositoryInterface

use App\Entity\Order\Order;

/**
 * @extends RepositoryInterface<Order>
 */
interface OrderRepositoryInterface extends RepositoryInterface

vasilvestre avatar Jun 19 '24 09:06 vasilvestre

And finally for a clean solution :

Following this tutorial : https://docs.sylius.com/en/1.12/customization/repository.html

App\Repository\OrderRepositoryInterface

<?php

declare(strict_types=1);

namespace App\Repository;

use App\Entity\Order\Order;
use Sylius\Component\Core\Repository\OrderRepositoryInterface as BaseOrderRepositoryInterface;

/**
 * @extends BaseOrderRepositoryInterface<Order>
 */
interface OrderRepositoryInterface extends \Sylius\Component\Core\Repository\OrderRepositoryInterface
{
}

The doctrine configuration is required :

parameters:
    doctrine:
        objectManagerLoader: tests/object-manager.php

This only work for Sylius 1.13+. There was no template phpdoc before. Maybe the @extends with ObjectManager would work

vasilvestre avatar Jun 19 '24 09:06 vasilvestre