-
Notifications
You must be signed in to change notification settings - Fork 1
PHP Cody Tutorial Exercise II
Exercise I introduced the core functionality of Cody. You can model business logic on an event map using Event Storming semantics and generate source code from it. So far we only generated an empty class. But Cody offers much more than that! In Exercise II you'll learn how to use Card Metadata to define a schema and derive class properties from it.
Execute docker-compose run --rm composer exercise2
to see what we have to do next.
We're asked to add a buildingId
and a name
property (both of type string
) to the AddBuilding
command. Now it would be easy to just open the class and add those two properties. But we should expand our code generation logic instead. This has some significant advantages:
- an InspectIO event map acts as documentation
- one can generate contracts like an OpenAPI schema from Card Metadata
- it is easier to discuss and design new features or refactorings when such details are included on the event map
Ok let's use a simple code generator implementation first to understand the basics. In a later tutorial part we'll look at some useful libraries that provide abstractions for advanced use cases.
Change the command hook in cody-tutorial/cody-bot/src/Hook/CommandHook.php
like this:
<?php
declare(strict_types=1);
namespace EventEngine\InspectioCody\Hook;
use EventEngine\InspectioCody\Board\BaseHook;
use EventEngine\InspectioCody\Http\Message\CodyResponse;
use EventEngine\InspectioCody\Http\Message\Response;
use EventEngine\InspectioGraphCody\Node;
use stdClass;
use function is_array;
use function json_decode;
final class CommandHook extends BaseHook //BaseHook provides some helper methods like writeFile()
{
/**
* @param Node $command Information about Command sticky received from InspectIO event map
* @param stdClass $context Context object populated in codyconfig.php
* @return CodyResponse Response sent back to InspectIO, shown in Cody Console
*/
public function __invoke(Node $command, stdClass $context): CodyResponse
{
$commandName = $command->name();
$commandFile = $commandName . '.php';
$commandPath = $context->path . '/Command/' . $commandFile;
// Get raw metadata info about command and convert it to associative array
$metadata = json_decode($command->metadata(), true);
$properties = "";
if(is_array($metadata)) {
// @TODO: some structural validation might be a good idea here
foreach ($metadata as $property => $type) {
// Each property-type-pair is appended to properties string
// and resulting string is inserted in the class template below
$properties.= " public $type $$property;\n";
}
}
$code = <<<CODE
<?php
declare(strict_types=1);
namespace Cody\Tutorial\Command;
class $commandName
{
$properties
}
CODE;
$this->writeFile($code, $commandPath);
return Response::fromCody(
"Command \"{$commandName}\" generated",
["Command written to {$commandPath}"]
);
}
}
Now we can switch to InspectIO and set the following metadata in JSON format to the AddBuilding
command:
{
"buildingId": "string",
"name": "string"
}
Metadata can be set by opening the Metadata Sidebar (choose Metadata from top menu) and selecting the appropriate card or sticky note. Metadata changes are saved automatically.
Once metadata is set we can trigger Cody again ...
... and validate the result by executing docker-compose run --rm composer exercise2
again:
Cards on an InspectIO event map can have additional information set as metadata. By default metadata is stored in JSON format. Metadata itself is schemaless, meaning users are free to define any structure. The structure can be defined and even be type checked using Metadata Templates. In a Cody hook you have access to raw metadata and use it for advanced code generation.
Join the community chat on Gitter.
No beta user yet? Request access in the community chat.