Download the PHP package grifart/scaffolder without Composer
On this page you can find all versions of the php package grifart/scaffolder. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download grifart/scaffolder
More information about grifart/scaffolder
Files in grifart/scaffolder
Package scaffolder
Short Description Class scaffolder. Write definition, generate simple value holders. Useful for trivial composite types used in event sourced applications - for commands, events and query definitions classes. This mostly supplements public readonly $properties
License MIT
Homepage http://www.grifart.cz
Informations about the package scaffolder
grifart/scaffolder – The (class) scaffolder project.
It was designed to generated classes with none to simple logic. Typical usage is:
- data transfer objects (DTOs),
- events in event-sourced model,
- simple value objects (simple logic can be embedded using
#[Preserve]
attribute – see below).
It is developed at gitlab.grifart.cz, automatically mirrored to GitHub and distributed over Composer packagist:grifart/scaffolder.
You can also watch introduction (in Czech) on 🎥 YouTube.
Installation
We recommend to use Composer:
Quick start
-
Create a definition file. Definition file must return a list of
ClassDefinition
s. By default, its name must end with.definition.php
. We commonly use just.definition.php
: -
Run scaffolder. You can provide the path to the definition file (or a directory which is then recursively searched for definition files) as an argument. It defaults to the current working directory if omitted.
The recommended way is to run the pre-packaged Composer binary:
Alternative way: Register scaffolder as a Symfony command into you app.
Alternatively, you can register the `Grifart\ClassScaffolder\Console\ScaffoldCommand` into your application's DI container and run scaffolder through *symfony/console*. This makes it easier to access your project's services and environment in definition files. *This is considered advanced usage and is not necessary in most cases.*-
Your class is ready. Scaffolder generates classes from definitions, one class per file, residing in the same directory as the definition file. By default, scaffolder makes the file read-only to prevent you from changing it accidentally.
-
Use static analysis tool such as PHPStan or Psalm to make sure that everything still works fine if you've changed any definition file.
- Make sure that you haven't accidentally changed any generated file by adding
composer exec scaffolder check .definition.php
to your CI workflow. The command fails if any generated class differs from its definition, and thus running the scaffolder would result in losing your changes.
Definition files
A definition file must return a list of ClassDefinition
s. The easiest way to create a definition is to use the definitionOf()
function:
The definitionOf()
accepts the name of the generated class and optionally a map of its fields and their types, and returns a ClassDefinition
. You can further add fields and capabilities to the definition.
Fields and types
Since scaffolder is primarily designed to generate various data transfer objects, fields are first-class citizens. Every field must have a type: scaffolder has an abstraction over PHP types and provides functions to compose even the most complex of types. It adds phpdoc type annotations where necessary so that static analysis tools can perfectly understand your code.
The available types are:
-
simple types such as
'int'
,'string'
,'array'
, etc.results in
-
class references via
::class
are resolved to the referenced class, interface or enum:results in
-
references to other definitions are supported and resolved:
results in
-
nullability can be expressed via
nullable()
:results in
-
lists can be created via
listOf()
:results in
-
key-value collections can be created via
collection()
:results in
-
any generic types can be represented via
generic()
:results in
-
complex array shapes can be described via
arrayShape()
:results in
-
similarly, tuples can be created via
tuple()
:results in
-
unions and intersections are supported as well:
results in
Capabilities
Fields on their own are not represented in the generated code, they just describe which fields the resulting class should contain. To add any behaviour to the class, you need to add capabilities to it. Scaffolder comes prepared with a bundle of capabilities for the most common use-cases:
-
properties()
generates a private property for each field:results in:
-
initializingConstructor()
generates a public constructor with property assignments. This works best when combined with theproperties()
capability:results in:
-
constructorWithPromotedProperties()
generates a public constructor with promoted properties. This can be used instead of the preceding two capabilities in PHP 8+ code:results in:
-
readonlyProperties()
makes properties or promoted parameters public and readonly:results in:
-
privatizedConstructor()
makes the class constructor private:results in:
-
namedConstructor($name)
creates a public static named constructor:results in:
-
getters()
generates public getters for all fields:results in:
-
setters()
generates public setters for all fields:results in:
-
immutableSetters()
generates public withers for all fields:results in:
-
implementedInterface()
adds animplements
clause to the generated class:results in:
⚠️ Please note that scaffolder DOES NOT check whether your class actually fulfills given interface. You can provide implementation using the
preservedAnnotatedMethods()
capability (see below).
Adding and preserving logic
Scaffolder regenerates your classes every time it runs. If you make any changes to the generated classes, you will lose them the next time you run scaffolder. (Scaffolder prevents this by making the generated files read-only, but that can be easily worked around.) However, even DTOs can contain some simple logic, for example concatenating the first and last name.
Consider the following definition:
It results in the generated class:
We want to add a getFullName()
method and preserve it when scaffolder runs next time. The trick is to mark the method with the #[Preserve]
attribute:
and add the preservedAnnotatedMethods()
capability to the definition:
The next time you run scaffolder, the getFullName()
method will be kept intact as long as it has the #[Preserve]
attribute.
Alternatively, you can use the preservedMethod($methodName)
capability that keeps only methods that are explicitly listed in the capability function.
⚠️ The method-preserving capabilities are best accompanied by
preservedUseStatements()
capability which makes sure that alluse
statements from the original file are preserved.
Custom capabilities
Capability is a very simple interface, so you can easily create and use your own:
ℹ️ Tip: If you need just single-purpuse capability, you can define it as a anonymous class. e.g.:
Do not repeat yourself
As the definition file is a plain old PHP file, you can use any language construct to your advantage. We commonly define functions which preconfigure capabilities and even fields for repeating patterns:
Such functions can then easily be reused throughout your definition files:
⚠️ Scaffolder relies on Composer autoloader. To be able to access your functions in definition files, you should add them into the
files
autoloading section incomposer.json
, or wrap them into static classes that can be automatically autoloaded by Composer. If you have your custom autoloader, please register this library as a command into your application. It will then use your custom environment.
All versions of scaffolder with dependencies
ext-tokenizer Version *
nette/finder Version ^2.5||^3.0
nette/php-generator Version ^4.0.1
nette/utils Version ^3.0||^4.0
symfony/console Version ^6.0||^7.0
symfony/filesystem Version ^6.0||^7.0