Skip to content

PHP Cody Tutorial Exercise II

Alexander Miertsch edited this page Feb 14, 2021 · 5 revisions

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.

Task

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

Expand Command Hook Logic

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}"]
        );
    }
}

Set Command Metadata in InspectIO

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:

Recap Exercise II

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.

Next - Exercise III