Biblioteca autônoma PHP para gerar um padrão de construtor a partir de uma classe.
Usando o compositor em seu projeto ou globalmente
composer require natitech/builder-generator
composer global require natitech/builder-generator
Você pode usar o binário para gerar um construtor próximo a uma classe:
/path/to/vendor/bin/generate-builder /path/to/class
OU você pode usá-lo dentro de outro script PHP:
Nati BuilderGenerator FileBuilderGenerator:: create ()-> generateFrom ( ' /path/to/class ' );
Isso irá gerar uma classe construtora além da classe construída.
Exemplo :
//From /path/to/MyClass.php file = the built class
class MyClass {
public int $ id ;
public string $ label ;
}
//This new file /path/to/MyClassBuilder.php will be generated = the builder class
class MyClassBuilder {
private int $ id ;
private string $ label ;
public function __construct ( Faker $ faker )
{
$ this -> id = $ faker -> number ();
$ this -> label = $ faker -> word ;
}
public function build (): MyClass
{
$ myClass = new MyClass ();
$ myClass -> id = $ this -> id ;
$ myClass -> label = $ this -> label ;
return $ myClass ;
}
//this will have to be generated by your IDE
public function withLabel ( string $ label ): self
{
$ this -> label = $ label ;
return $ this ;
}
}
//The ultimate goal is to use this in tests
/** @test */
public function canTestSomething ()
{
$ this -> assertEquals (
' test ' ,
$ this -> service -> something ( $ this -> myClass ()-> withLabel ( ' used in test ' )-> build ())
);
}
private function myClass (): MyClassBuilder
{
return new MyClassBuilder ( Faker Factory:: create ());
}
A classe do construtor pode precisar receber atualizações sobre estilo de código, usos de faker, tipos inferidos, namespace, etc. Além disso, para evitar a produção de código não utilizado, não há configuradores para propriedades do construtor. Seu IDE deve ser capaz de consertar tudo isso facilmente.
O gerador tentará detectar tipos de propriedades (tipos php, tipos phpdoc, atributos de doutrina ou anotações) e tentará detectar o método faker apropriado com base no tipo e no nome das propriedades.
Você pode usar a opção --faker para tentar definir os valores mais relevantes. Nesse caso, Faker será usado como uma dependência da classe construtora.
Existem muitas estratégias para construir uma classe: propriedades públicas, setter (fluente ou não), construtor. Uma breve explicação sobre cada estratégia é fornecida abaixo, mas você pode preferir ler os testes unitários para entendê-los completamente.
Por padrão, o gerador tentará detectar a estratégia mais usada dentro da classe construída e a utilizará para toda a classe construtora. Se você estiver usando setters em todas as propriedades, exceto uma, o gerador usará setters nas propriedades que o suportam e ignorará a propriedade que não o suporta.
Mas você também pode passar uma estratégia explicitamente usando a opção '--strategy'.
Quando as propriedades são públicas. Veja o exemplo acima.
Quando as propriedades são protegidas/privadas e definidas dentro do método __construct.
//Built class
class MyClass {
private int $ id ;
public function __construct ( int $ id , private string $ label )
{
$ this -> id = $ id ;
}
}
//Builder class
class MyClassBuilder {
private int $ id ;
private string $ label ;
public function __construct ( Faker $ faker )
{
$ this -> id = $ faker -> number ();
$ this -> label = $ faker -> word ;
}
public function build (): MyClass
{
return new MyClass (
$ this -> id ,
$ this -> label
);
}
}
Quando as propriedades são protegidas/privadas, mas podem ser definidas usando setters públicos. Os setters podem ser fluentes ou não.
//Built class
class MyClass {
protected int $ id ;
protected string $ label ;
public function setId ( int $ id )
{
$ this -> id = $ id ;
return $ this ;
}
public function setLabel ( string $ label )
{
$ this -> label = $ label ;
return $ this ;
}
}
//Builder class
class MyClassBuilder {
private int $ id ;
private string $ label ;
public function __construct ( Faker $ faker )
{
$ this -> id = $ faker -> number ();
$ this -> label = $ faker -> word ;
}
public function build (): MyClass
{
return ( new MyClass ())
-> setId ( $ this -> id )
-> setLabel ( $ this -> label );
}
}
Esta é uma maneira menos convencional de construir uma classe. Nesse caso, as propriedades são protegidas/privadas e definidas por métodos de domínio público. Portanto, não há uma maneira fácil de definir a classe construída em um determinado estado. Se, para fins de teste, você deseja definir rapidamente o estado das propriedades desse objeto por propriedades, uma solução é adicionar um método build() estático na classe construída.
//Built class
class MyClass {
private int $ id ;
private ? string $ label = null ;
public static function create ( int $ id ): self
{
$ myClass = new self ();
$ myClass -> id = $ id ;
return $ myClass ;
}
public function updateLabel ( string $ label ): self
{
$ this -> label = $ label ;
return $ this ;
}
//This method will have to be generated by you IDE from the builder class
//It allows you to hack the state of this object without relying on its public interface
public static function build ( int $ id , string $ label ): self
{
$ myClass = new self ();
$ myClass -> id = $ this -> id ;
$ myClass -> label = $ this -> label ;
return $ myClass ;
}
}
//Builder class
class MyClassBuilder {
private int $ id ;
private string $ label ;
public function __construct ( Faker $ faker )
{
$ this -> id = $ faker -> number ();
$ this -> label = $ faker -> word ;
}
public function build (): MyClass
{
return MyClass:: build (
$ this -> id ,
$ this -> label
);
}
}
Para criar uma estratégia personalizada:
NatiBuilderGeneratorPropertyPropertyBuildStrategy
e adicioná-lo a NatiBuilderGeneratorPropertyPropertyBuildStrategyCollection
. Isto decidirá COMO as propriedades serão construídas.NatiBuilderGeneratorAnalyzerBuildableClassAnalyzer::getWriteStrategies()
. Isto decidirá QUANDO as propriedades poderão ser construídas com esta estratégia. Você pode usar esta ferramenta como uma ferramenta externa em seu IDE.
Para usuários do PHPStorm, consulte https://www.jetbrains.com/help/phpstorm/configurando-third-party-tools.html. Exemplo de configuração: