Welcome to the Accessor Generator composer plugin.
The goal of this plugin is to provide dynamically generated get, set, add, remove accessor methods for Classes based on information that we can read from the doc comment. Currently we can process Doctrine ORM annotations.
Since the code is automatically generated you do not have to (unit) test it and it will be very consistent with a lot of added boilerplate code that will make your code fail early if you happen to use the wrong type or number of arguments with the generated functions.
- Imports through grouped
use
statements is not supported. (https://wiki.php.net/rfc/group_use_declarations) - Scalar typehints is not being added (https://wiki.php.net/rfc/scalar_type_hints_v5)
Add hostnet/accessor-generator-plugin-lib
to your composer.json
and run
composer require hostnet/accessor-generator-plugin-lib
If you want to invoke generation after installing you can run php composer.phar dump-autoload
.
Add -vv
to the dump-autoload command for more verbosity.
<?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
*
* @var int
*/
private $id;
// ...
}
The files will be generated in a subdirectory and namespace (Generated) relative to the current file. This file can be included as trait.
It is possible to disable generation of certain accessor methods by specifying them in the annotation.
/**
* @AG\Generate(add=false,set=false,remove=false,get=false,is=false)
*/
Is
is an alias for get. If your property is of type boolean an isProperty
method is
generated instead of a getProperty
method. For ORM\GeneratedValue
properties, no
setters will be generated. Note that the example above will generate no code at all.
If no configuration is specified, the default behaviour for all scalar typed properties is that a getter and a settter method will be generated. Adders and Removers will be generated when the type is iterable (e.g. DoctrineCollection or array).
To use asymmetric encryption on a column's value add the 'encryption_alias' field to the Generate
annotation.
Also make sure the type of the database column has a big enough length. At least 1064 for key and IV is needed,
plus the length of the sealed data itself.
/**
* @AG\Generate(encryption_alias="database.table.column")
*/
private $encrypted_variable;
The alias used there should be added to the application's composer.json as follows:
"extra": {
"accessor-generator": {
<encryption_alias>: {
"public-key": <public_key_file>
"private-key": <private_key_file>
...
In order to encrypt or decrypt data, a valid private and public key must be specified.
- The public key is needed to encrypt data.
- The private key is needed to decrypt data.
In order to start encrypting data, a public key is necessary. However, you will first need a private key in order to
extract a public key from it. We can use the openssl
tool to do so:
Creating a key:
$ openssl genrsa -out database.table.column.private_key.pem 2048
Extracting a public key from a private key:
$ openssl rsa -in database.table.column.private_key.pem -pubout > database.table.column.public_key.pem
If the application has to encrypt, add the public key. If the application has to decrypt, add the private key. If the application has to do both, add both.
The <public_key_file>
and <private_key_file>
values have to contain the file paths to the keys relative
to the composer.json file.
Do not forget to use the setter method in the constructor to trigger the encryption of the given value instead of assigning a value to the property directly. .
<?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.
}
}
Since version 2.8.0, the support of accessor generation of parameterized collections has been added. With this addition,
the requirement of PHP 7.1 has been added due to the need of ReflectionConstant
, which was added in PHP 7.1.
Imagine having an entity that holds an ArrayCollection
to another entity that holds parameters. For example:
$task = new Task();
$task->setParam(MyParamEnum::I_CLIENT_ID, 123456);
echo $task->getParam(MyParamEnum);
// 12345
As you might notice, although the parameter name is prefixed with I_
- which would indicate that we're dealing with
an integer - you can still set any data-type you want as long as the implementation of setParam
supports it. If you're
working in a large team or in larger projects, not everybody might be aware that an enum class exists that defines all
common parameter names that should be used throughout the application for this entity.
Version 2.8.0 introduces the ability to generate accessors for enum classes.
The owning entity - Task
in the example above - must implement a property that is of type ArrayCollection
which
defines a OneToMany
relationship with a Parameter
-entity.
class Task
{
// ...
/**
* @ORM\OneToMany(targetEntity="Parameter", cascade={"persist"})
*/
private $parameters;
// ...
}
The Parameter
entity must implement the following:
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;
}
}
The "enum class" only consists of public constants that use a prefix in their names to denote the data type of the values they hold in the database.
The following types are supported:
prefix | type | example |
---|---|---|
S_ |
string | "foobar" |
I_ |
integer | 1234 |
F_ |
float | 3.14 |
B_ |
boolean | true |
A_ |
array | array |
Now, lets take the following example for an enum class with some parameters:
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/';
}
Now that we have our three classes (Task
, Parameter
and MyTaskParamNames
), we can start generating code.
With version 2.8.0 comes the Enumerator
annotation which can be used inside the existing Generate
annotation.
Upgrading from 2.8.0 to 2.8.1: The "name" setting in the annotation has been changed to "property" to be more consistent. Since 2.8.1, the ability to add inline enumerators through other class properties has been added. See below for more information.
Taking the code that we just wrote in the examples above, we can generate an accessor method for MyTaskParamNames
by modifying the annotation of the parameters
property of our Task
class.
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;
}
Once the code is generated, you will now have a newly generated class called MyTaskParamNamesEnum
in the
Generated
directory (and namespace) relative to the namespace of MyTaskParamNames
. An accessor for this class is
generated using the property
settting in the TaskMethodsTrait
.
The accessor for this enum based on the code above will be called getMyParams()
. You can give this any name you want
as long as it is suitable for a method name.
Once the code is generated, you'll have access to 5 methods per parameter:
$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();
All methods are strictly typed based on their prefix in the enum class.
Have a look at the ParamNameEnum class to see an example of the generated code.
WARNING: The default visiblity of accessor methods (get/is/set/add/remove) will be set to
none
if enumerators are used. If you still need these methods to be generated, you'll have to specify them explicitly.
As you might have noticed, the enumerators
property of the Generate
annotation accepts a list
of one or more Enumerator
annotations. You can specify one ore more enum classes that utilize
the same collection for their "storage".
If your annotation looks like this:
/**
* @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;
The generator will now create two accessors for these parameter enumerators that you can use like this:
$task->getMyParams()->hasClientId(); // From MyTaskParamNames
$task->getBetterParams()->setFoobar(1234); // From BetterParamNames
You can also define enumerators outside the @Generate
annotation. If used in combination with the entity-plugin-lib
,
it is possible to define a trait
that holds an enumerator property that refers to a collection on your entity.
Lets say we want to add an extra enumerator to our - already existing - Task entity that we have written before.
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;
}
The name
setting refers to the ArrayCollection property that holds all parameters owned by that entity.
Once the code is generated, you can now invoke the enumerator like any other:
<?php
$task = new Task();
$task->getSomeExtraParams()->...
// Whilst still having access to the already existing enumerators that we defined before.
$task->getMyParams();
$task->getBetterParams();
Make sure to include the trait Generated\TaskTrait
- or whatever name your entity has - in the task entity to make
this work. So,
<?php
class Task
{
use Generated\TaskMethodsTrait;
}
becomes:
<?php
class Task
{
use Generated\TaskTrait;
use Generated\TaskMethodsTrait;
}
Please refer to the entity-plugin-lib for more information.