hostnet/accessor-generator-plugin-lib

动态生成get、set、add、remove方法。


README

欢迎使用Accessor Generator composer插件。

目标

此插件的目标是为基于我们可从文档注释中读取的信息的类提供动态生成的get、set、add、remove访问器方法。目前我们可以处理Doctrine ORM注解。

由于代码是自动生成的,你不需要对其进行(单元)测试,并且它将与大量附加的样板代码非常一致,如果不幸使用错误的类型或参数数量使用生成的函数,你的代码将会在早期失败。

限制

安装

hostnet/accessor-generator-plugin-lib添加到你的composer.json,并运行composer require hostnet/accessor-generator-plugin-lib

如果你想在安装后调用生成,你可以运行php composer.phar dump-autoload。向dump-autoload命令添加-vv以获得更多详细输出。

用法

<?php
namespace Hostnet\Product\Entity;

use Doctrine\ORM\Mapping as ORM;
use Hostnet\Component\AccessorGenerator\Annotation as AG;

/**
 * @ORM\Entity
 * @ORM\Table(name="periode")
 */
class Period
{
    use Generated\PeriodMethodsTrait;                   // This is the file that is generated with the
                                                        // accessor methods inside.
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(name="id", type="integer")
     * @AG\Generate                                     // Here you ask methods to be generated
     *
     */
    private int $id;

    // ...
}

文件将在当前文件相对的子目录和命名空间(Generated)中生成。此文件可以包含为特质。

指定要生成的函数

你可以通过在注解中指定它们来禁用某些访问器方法的生成。

/**
 * @AG\Generate(add="none",set="none",remove="none",get="none",is="none")
 */

Is是get的别名。如果你的属性是布尔类型,则将生成isProperty方法而不是getProperty方法。对于ORM\GeneratedValue属性,不会生成setter。**注意,上述示例将不会生成任何代码**。

如果没有指定配置,则默认行为是对于所有标量类型属性,都会生成get和set方法。当类型是可迭代的(例如,DoctrineCollection或数组)时,将生成adders和removers。

加密

要在列的值上使用非对称加密,请将'encryption_alias'字段添加到Generate注解中。同时请确保数据库列的类型有足够大的长度。密钥和IV至少需要1064个字符,加上加密数据的长度。

/**
 * @AG\Generate(encryption_alias="database.table.column")
 */
 private $encrypted_variable;

在该处使用的别名应添加到应用程序的composer.json中,如下所示

"extra": {
     "accessor-generator": {
         <encryption_alias>: {
             "public-key": <public_key_file>
             "private-key": <private_key_file>
...

为了加密或解密数据,必须指定有效的私钥和公钥。

  • **公钥**用于加密数据。
  • **私钥**用于解密数据。

为了开始加密数据,需要一个公钥。但是,你需要一个私钥才能从中提取公钥。我们可以使用openssl工具来做。

创建密钥

$ openssl genrsa -out database.table.column.private_key.pem 2048

从私钥中提取公钥

$ openssl rsa -in database.table.column.private_key.pem -pubout > database.table.column.public_key.pem

如果应用程序需要加密,添加公钥。如果应用程序需要解密,添加私钥。如果应用程序需要两者都做,添加两者。

<public_key_file><private_key_file>的值必须包含相对于composer.json文件的密钥的文件路径。

不要忘记在构造函数中使用setter方法来触发给定值的加密,而不是直接将该值赋给属性。

<?php
class MyEntity
{
    /**
     * @AG\Generate(encryption_alias="<encryption_alias>")
     */
    private $my_value;
    
    public function __construct(string $my_value)
    {
        $this->my_value = $my_value;  // No encryption is taking place.
        $this->setMyValue($my_value); // The value is now encrypted in the field.
    }
}

使用ENUM类参数

自2.8.0版本起,增加了对参数化集合访问器生成的支持。由于需要PHP 7.1中新增的ReflectionConstant,因此增加了对PHP 7.1的支持。

想象一下有一个实体持有另一个实体的ArrayCollection,该实体持有参数。例如

$task = new Task();
$task->setParam(MyParamEnum::I_CLIENT_ID, 123456);

echo $task->getParam(MyParamEnum); 
// 12345

正如你可能注意到的,尽管参数名以I_前缀开头——这表明我们正在处理整数——但只要setParam的实现支持,你仍然可以设置任何你想要的数据类型。如果你在一个大型团队或大型项目中工作,可能并不是每个人都意识到存在一个枚举类,该类定义了整个应用程序中应该用于此实体的所有常用参数名。

2.8.0版本引入了生成枚举类访问器的功能。

要求

拥有实体——上面的例子中的Task——必须实现一个类型为ArrayCollection的属性,该属性
Parameter实体定义了一个OneToMany关系。

class Task
{
    // ...
    
    /**
     * @ORM\OneToMany(targetEntity="Parameter", cascade={"persist"})
     */
    private $parameters;

    // ...
}

Parameter实体必须实现以下

use Hostnet\Component\AccessorGenerator\Enum\EnumeratorCompatibleEntityInterface;

class Parameter implements EnumeratorCompatibleEntityInterface
{
    /**
     * @ORM\ManyToOne(targetEntity="Task")
     */
    private $owner;
    
    /**
     * @ORM\Column(type="string")
     */
    private $name;
    
    /**
     * @ORM\Column(type="string")
     * @AG\Generate()
     */
    private $value;
    
    // This signature is a requirement for enum accessor generation.
    public function __construct($task, string $name, ?string $value)
    {
        $this->owner = $task;
        $this->name  = $name;
        $this->value = $value;
    }
}

枚举类

"枚举类"仅包含使用前缀命名的公共常量,该前缀表示它们在数据库中持有的值的类型。

以下类型受到支持

现在,让我们以以下枚举类示例为例,其中包含一些参数

class MyTaskParamNames
{
    /**
     * Represents the client if the task is currently runnnig for.
     */
    public const I_CLIENT_ID = 'CLIENT_ID';
    
    /**
     * An awesome URL.
     */
    public const S_AWESOME_URL = 'https://www.hostnet.nl/';
}

现在我们已经有了三个类(《Task》、《Parameter》和《MyTaskParamNames》),我们可以开始生成代码。

"枚举器"注解

2.8.0版本带来了Enumerator注解,可以在现有的Generate注解中使用。

从2.8.0升级到2.8.1:注解中的"名称"设置已更改为"属性",以提高一致性。从2.8.1版本开始,增加了通过其他类属性添加内联枚举器的功能。更多信息请见下文。

通过修改我们上面示例中编写的代码,我们可以通过修改我们的Task类的parameters属性的注解来为MyTaskParamNames生成一个访问器方法。

class Task
{
    use Generated\TaskMethodsTrait;

    /**
     * @ORM\OneToMany(targetEntity="Parameter", cascade={"persist"})
     * @AG\Generate(enumerators={
     *     @AG\Enumerator("MyTaskParamNames", property="my_params")
     * })
     */
    private $property;
    
    /**
     * @var Generated\MyTaskParamNamesEnum
     */
    private $my_params;
}

一旦生成代码,你将现在有一个新创建的名为MyTaskParamNamesEnum的类,位于相对于MyTaskParamNames命名空间的Generated目录(和命名空间)中。使用TaskMethodsTrait中的property设置生成该类的访问器。

根据上面的代码,此枚举的访问器将被称为getMyParams()。你可以给它任何你想要的名称,只要它适合一个方法名。

一旦生成代码,你将可以访问每个参数5个方法

$task = new Task();

// hasClientId() will check if a parameter with the name I_CLIENT_ID exists in the collection of parameters belonging to
// this Task instance.
if (! $task->getMyParams()->hasClientId()) {
    
    // Create the I_CLIENT_ID parameter with a value of 1234.
    $task->getMyParams()->setClientId(1234);  
}

// Update the value of the existng parameter.
$task->getMyParams()->setClientId(999);

// Retrieve the value
$client_id = $task->getMyParams()->getClientId();

// Clear the value (keeps the element in the collection, but nullifies the value).
// hasClientId() will now return FALSE as if the parameter doesn't exist.
$task->getMyParams()->clearClientId();

// Remove the element entirely, effectively dropping the record from the database.
$task->getMyParams()->removeClientId();

所有方法都是根据枚举类中的前缀进行严格类型化的。

请查看ParamNameEnum类,以查看生成的代码示例。

警告:如果使用枚举器,访问器方法(get/is/set/add/remove)的默认可见性将设置为none。如果您仍然需要生成这些方法,您必须明确指定它们。

多个枚举器

正如你可能已经注意到的,Generate注解的enumerators属性接受一个包含一个或多个Enumerator注解的列表。您可以指定一个或多个使用相同"存储"集合的枚举类。

如果您的注解看起来像这样

/**
 * @AG\Generate(enumerators={
 *     @AG\Enumerator("MyTaskParamNames", property="my_params"),
 *     @AG\Enumerator("BetterParamNames", property="better_params")
 * });
 */
 private $parameters;
 
 /**
  * @var Generated\MyTaskParamNamesEnum
  */
 private $my_params;
 
 /**
  * @var Generated\BetterParamNamesEnum
  */
 private $better_params;

生成器现在将为这些参数枚举器创建两个访问器,您可以像这样使用它们

$task->getMyParams()->hasClientId();       // From MyTaskParamNames
$task->getBetterParams()->setFoobar(1234); // From BetterParamNames

分离的枚举器访问器生成

您也可以在 @Generate 注解外部定义枚举器。如果与 entity-plugin-lib 结合使用,可以定义一个包含枚举属性的特性,该属性引用您实体上的集合。

假设我们想要向之前已经编写过的 - 已存在的 - Task 实体添加一个额外的枚举器。

use Hostnet\Component\AccessorGenerator\Annotation as AG;

trait TaskTrait
{
    use Generated\TaskTraitMethodsTrait;

    /**
     * @AG\Enumerator("\My\Namespace\MyExtraParamName", name="parameters")
     * @var \My\Namespace\Generated\MyExtraParamNameEnum
     */
    private $some_extra_params;
}

name 设置指的是包含该实体所有参数的 ArrayCollection 属性。

一旦代码生成,您现在可以像调用其他枚举器一样调用它。

<?php

$task = new Task();
$task->getSomeExtraParams()->...

// Whilst still having access to the already existing enumerators that we defined before.
$task->getMyParams();
$task->getBetterParams();

请确保将特性 Generated\TaskTrait - 或您实体使用的任何名称 - 包含在任务实体中以便此操作生效。所以,

<?php
class Task
{
    use Generated\TaskMethodsTrait;
}

变成了

<?php
class Task
{
    use Generated\TaskTrait;
    use Generated\TaskMethodsTrait;
}

请参阅 entity-plugin-lib 获取更多信息。