Esta extensão fornece os seguintes recursos:
findBy* , findOneBy* e countBy* no EntityRepository.findBy , findBy* , findOneBy , findOneBy* , count e countBy* Chamadas de método.EntityRepository<MyEntity> corretamente em phpdocs para mais uma inferência de métodos chamados no repositório.DoctrineORMEntityManager::getRepository() .DoctrineORMEntityManager::find , getReference e getPartialReference quando o nome da classe de entidade Foo::class é fornecido como o primeiro argumentomatching ausente na DoctrineCommonCollectionsCollection . Isso pode ser desligado definindo parameters.doctrine.allCollectionsSelectable para false .allowNullablePropertyForRequiredField: true .DoctrineORMQuery::getResult , getOneOrNullResult , getSingleResult , toIterable e execute no modo HYDRATE_OBJECT (veja abaixo). Para usar esta extensão, requer no compositor:
composer require --dev phpstan/phpstan-doctrineSe você também instalar o Phpstan/Extension-Installer, está tudo pronto!
Se você não quiser usar phpstan/extension-installer , inclua Extension.neon na configuração do Phpstan do seu projeto:
includes :
- vendor/phpstan/phpstan-doctrine/extension.neon Se você estiver interessado na validação DQL/QueryBuilder, inclua também rules.neon (você também precisará fornecer o objectManagerLoader , veja abaixo):
includes :
- vendor/phpstan/phpstan-doctrine/rules.neon Se seus repositórios tiverem uma classe base comum, você poderá configurá -lo em seu phpstan.neon e Phpstan verão métodos adicionais que você definir nele:
parameters :
doctrine :
ormRepositoryClass : MyAppDoctrineBetterEntityRepository
odmRepositoryClass : MyAppDoctrineBetterDocumentRepositoryVocê pode optar por uma análise mais avançada, fornecendo o gerenciador de objetos a partir de seu próprio aplicativo. Isso permitirá a validação do DQL:
parameters :
doctrine :
objectManagerLoader : tests/object-manager.phpExemplo para Symfony 4:
// tests/object-manager.php
use App Kernel ;
require __DIR__ . ' /../config/bootstrap.php ' ;
$ kernel = new Kernel ( $ _SERVER [ ' APP_ENV ' ], ( bool ) $ _SERVER [ ' APP_DEBUG ' ]);
$ kernel -> boot ();
return $ kernel -> getContainer ()-> get ( ' doctrine ' )-> getManager ();Exemplo para Symfony 5:
// tests/object-manager.php
use App Kernel ;
use Symfony Component Dotenv Dotenv ;
require __DIR__ . ' /../vendor/autoload.php ' ;
( new Dotenv ())-> bootEnv ( __DIR__ . ' /../.env ' );
$ kernel = new Kernel ( $ _SERVER [ ' APP_ENV ' ], ( bool ) $ _SERVER [ ' APP_DEBUG ' ]);
$ kernel -> boot ();
return $ kernel -> getContainer ()-> get ( ' doctrine ' )-> getManager (); Essa extensão pode inferir o tipo de resultado de consultas DQL quando um objectManagerLoader é fornecido.
Exemplos:
$ query = $ entityManager -> createQuery ( ' SELECT u FROM AcmeUser u ' );
$ query -> getResult (); // array<AcmeUser>
$ query = $ entityManager -> createQuery ( ' SELECT u.id, u.email, u.name FROM AcmeUser u ' );
$ query -> getResult (); // array<array{id: int, email: string, name: string|null}>
$ query = $ entityManager -> createQuery ( '
SELECT u.id, u.email, COALESCE(u.name, "Anonymous") AS name
FROM AcmeUser u
' );
$ query -> getSingleResult (Query:: HYDRATE_OBJECT ); // array{id: int, email: string, name: string}>
$ query = $ entityManager -> createQueryBuilder ()
-> select ( ' u ' )
-> from (User::class, ' u ' )
-> getQuery ();
$ query -> getResult (); // array<AcmeUser>As consultas são analisadas estaticamente e não exigem um servidor de banco de dados em execução. Isso utiliza o analisador DQL e os metadados da doutrina.
A maioria dos recursos do DQL é suportada, incluindo GROUP BY , INDEX BY , DISTINCT , todos os sabores de JOIN , expressões aritméticas, funções, agregações, NEW , etc. Sub -perguntas ainda não são suportadas (o tipo inferido será mixed ).
Se SUM(e.column) é buscada como float , numeric-string ou int depende muito dos drivers, sua configuração e versão PHP. Esta extensão cordea automaticamente sua configuração e fornece resultados bastante precisos para pdo_mysql , mysqli , pdo_sqlite , sqlite3 , pdo_pgsql e pgsql .
O método getResult é suportado quando chamado sem argumento, ou com o argumento do hidratemode definido como Query::HYDRATE_OBJECT :
$ query = $ entityManager -> createQuery ( ' SELECT u FROM AcmeUser u ' );
$ query -> getResult (); // array<User>
$ query -> getResult (Query:: HYDRATE_OBJECT ); // array<User> Os métodos getOneOrNullResult , getSingleResult , toIterable e execute são suportados quando o argumento do hidratemode é explicitamente definido como Query::HYDRATE_OBJECT :
$ query = $ entityManager -> createQuery ( ' SELECT u FROM AcmeUser u ' );
$ query -> getOneOrNullResult (); // mixed
$ query -> getOneOrNullResult (Query:: HYDRATE_OBJECT ); // User Isso se deve ao design da classe Query , impedindo a determinação do modo de hidratação usado por essas funções, a menos que seja especificado explicitamente durante a chamada.
Nem todo QueryBuilder pode ser analisado estaticamente, aqui estão poucos conselhos para maximizar o tipo de inferir:
select / join / from / set )Você pode permitir a reportagem de lugares onde inferir não está disponível por:
parameters :
doctrine :
reportDynamicQueryBuilders : true Se o seu aplicativo usar tipos de doutrina personalizados, você poderá escrever seus próprios descritores para analisá -los corretamente. Os descritores de tipo implementam a interface PHPStanTypeDoctrineDescriptorsDoctrineTypeDescriptor que se parece com o seguinte:
<?php
public function getType(): string ;
public function getWritableToPropertyType(): Type ;
public function getWritableToDatabaseType(): Type ;getType() simplesmente retorna o nome da classe do tipo personalizado.getWritableToPropertyType() retorna o tipo de phpstan que o tipo personalizado gravará no campo de propriedade da entidade. Basicamente, é o tipo de retorno do método convertToPHPValue() do tipo personalizado.getWritableToDatabaseType() retorna o tipo de phpstan que pode ser gravado no campo de propriedade da entidade no tipo personalizado. Novamente, basicamente, é o tipo permitido para o primeiro argumento do convertToDatabaseValue() Custom Geralmente, pelo menos para a maioria dos tipos nativos da doutrina, esses dois últimos métodos retornam o mesmo tipo, mas nem sempre é o caso. Um exemplo seria o tipo datetime , que permite definir qualquer DateTimeInterface para o campo Propriedade, mas sempre conterá o tipo DateTime quando carregado no banco de dados.
Os descritores de tipos não precisam lidar com tipos anuláveis, pois são adicionados/removidos de forma transparente dos tipos do descritor, conforme necessário. Portanto, você não precisa devolver o tipo de sindicato do seu tipo personalizado e NullType dos métodos do descritor, mesmo que o seu tipo personalizado permita null .
Se os métodos convertToPHPValue() e convertToDatabaseValue() tiverem, você não precisará escrever seu próprio descritor para ele. O PHPStanTypeDoctrineDescriptorsReflectionDescriptor pode analisar os tipos e fazer o resto para você.
Se o pai do seu tipo for um dos não abstratos da doutrina, ReflectionDescriptor reutilizará seu descritor mesmo para resolução de expressão (por exemplo, AVG(t.cost) ). Por exemplo, se você estender DoctrineDBALTypesDecimalType , ele saberá que o sqlite busca isso como float|int e outros drivers como numeric-string . Se você estender apenas DoctrineDBALTypesType , você deve usar o descritor personalizado e, opcionalmente, implementar DoctrineTypeDriverAwareDescriptor para fornecer resolução específica do driver.
Quando você escreve um descritor de tipo personalizado, você deve informar o Phpstan sobre isso. Adicione algo assim no seu phpstan.neon :
services :
-
class : MyCustomTypeDescriptor
tags : [ phpstan.doctrine.typeDescriptor ]
# in case you are using the ReflectionDescriptor
-
factory : PHPStanTypeDoctrineDescriptorsReflectionDescriptor ( ' MyAppMyCustomTypeName ' )
tags : [ phpstan.doctrine.typeDescriptor ]Se você deseja ter certeza de que nunca esquece o descritor para algum tipo personalizado, pode ativar:
parameters :
doctrine :
reportUnknownTypes : trueIsso causa falhas quando sua entidade usa tipo personalizado sem descritor:
#[Entity]
abstract class Uuid7Entity
{
#[Id]
#[Column(type: Uuid7Type:: NAME )] // reported when descriptor for such type is missing
private Uuid7 $ hsCode ; Qualquer função DQL personalizada que implementa TypedExpression da doutrina é entendida por esta extensão e é inferida com o tipo usado no método getReturnType() . Todas as outras funções DQL personalizadas são inferidas como mixed . Observe que você não pode usar StringType nativo para lançar (e inferir) os resultados da string (consulte o problema ORM).
use Doctrine DBAL Types Type ;
use Doctrine DBAL Types Types ;
use Doctrine ORM Query AST TypedExpression ;
use Doctrine ORM Query AST Functions FunctionNode ;
use Doctrine ORM Query Parser ;
use Doctrine ORM Query SqlWalker ;
use Doctrine ORM Query TokenType ;
class Floor extends FunctionNode implements TypedExpression
{
private AST Node | string $ arithmeticExpression ;
public function getSql ( SqlWalker $ sqlWalker ): string
{
return ' FLOOR( ' . $ sqlWalker -> walkSimpleArithmeticExpression ( $ this -> arithmeticExpression ) . ' ) ' ;
}
public function parse ( Parser $ parser ): void
{
$ parser -> match (TokenType:: T_IDENTIFIER );
$ parser -> match (TokenType:: T_OPEN_PARENTHESIS );
$ this -> arithmeticExpression = $ parser -> SimpleArithmeticExpression ();
$ parser -> match (TokenType:: T_CLOSE_PARENTHESIS );
}
public function getReturnType (): Type
{
return Type:: getType (Types:: INTEGER );
}
} Os arquivos Stub no Phpstan-Doctrine vêm com muitos parâmetros marcados com literal-string . Este é um tipo focado na segurança que apenas permite que as strings literais escritas no código sejam transmitidas para esses parâmetros.
Isso reduz o risco de injeção de SQL porque as seqüências dinâmicas da entrada do usuário não são aceitas no lugar da literal-string .
Um exemplo em que esse tipo é usado é o parâmetro $sql na DoctrineDbalConnection::executeQuery() .
Para ativar esse tipo avançado de phpstan-doutorado, use este parâmetro de configuração:
parameters :
doctrine :
literalString : true