thamtech/yii2-yaml

A Yaml helper for Yii2

Fund package maintenance!
Liberapay

Installs: 5 893

Dependents: 2

Suggesters: 0

Security: 0

Stars: 1

Watchers: 2

Forks: 1

Open Issues: 0

Type:yii2-extension

v1.0.0 2021-06-13 23:48 UTC

This package is auto-updated.

Last update: 2024-06-14 06:54:13 UTC


README

Yii2 Yaml provides a Yaml helper and a Yaml parser/dumper extension.

The Yaml helper provdesencode(), decode() and errorSummary() similar to yii\helpers\Json.

Yii2 Yaml also includes an extension of the symfony/yaml library that supports custom yaml tags representing UnsetArrayValue and ReplaceArrayValue, as well as allowing you to attach events to handle any other custom yaml tags.

For license information check the LICENSE-file.

Installation

The preferred way to install this extension is through composer.

php composer.phar require --prefer-dist thamtech/yii2-yaml

or add

"thamtech/yii2-yaml": "*"

to the require section of your composer.json file.

Background

The goal of this Yii2 Yaml extension is to support Yaml in Yii2 the way support for JSON is built-in. A Yaml helper is introduced to match the API of Yii2's built-in Json helper.

Furthermore, we have extended the symfony/yaml library to support decoding and encoding of ReplaceArrayValue and UnsetArrayValue objects for use with Yii's ArrayHelper::merge() method. You can use Yii2 event handlers to process custom tags as they are parsed/decoded and pre-process objects before they are dumped/encoded.

Yii2 uses encode/decode terminology in the Json helper, while symfony/yaml uses dump/parse terminology. Our helper is consistent with the Json helper in using the encode/decode terminology.

Usage

Decoding/Parsing

Example Yaml:

people:
    john:
        id: 1
        name: John
    # A value must be associated with a tag: either a block value (indented
    # section under the key) or an inline value.
    # Here, we demsonstrate a block value following the !yii/helpers/ReplaceArrayValue/tag:
    bob: !yii/helpers/ReplaceArrayValue
        id: 1001
        name: Bob
    # The `unsetArrayValue` handler requires that the associated value be
    # empty, so use an inline `{}`, `[]`, `false`, or `null` value.
    jane: !yii/helpers/UnsetArrayValue null
    susan: !lookupIdFromEmployeeNumber
        employee_number: 1234
        name: Susan

Example decoding using the Yaml helper:

<?php
use thamtech\yaml\helpers\Yaml;

$data = Yaml::decode($yaml);
print_r($data);

# Array
# (
#     [people] => Array
#         (
#           [john] => Array
#               (
#                   [id] => 1
#                   [name] => John
#               )
#           [bob] => yii\helpers\ReplaceArrayValue Object
#               (
#                   [value] => Array
#                       (
#                           [id] => 1001
#                           [name] => Bob
#                       )
#               )
#           [jane] => yii\helpers\UnsetArrayValue Object
#               (
#               )
#           [susan] => Symfony\Component\Yaml\Tag\TaggedValue Object
#               (
#                   [tag:Symfony\Component\Yaml\Tag\TaggedValue:private] => lookupIdFromEmployeeNumber
#                   [value:Symfony\Component\Yaml\Tag\TaggedValue:private] => Array
#                       (
#                           [employee_number] => 1234
#                           [name] => Susan
#                       )
#               )
#         )
# )

In the example above, you can see that the keys tagged with !yii/helpers/ReplaceArrayValue and !yii/helpers/UnsetArrayValue were automatically replaced with the helper objects ready for use with the ArrayHelper::merge() method.

In order to add your own handlers for tags like the !lookupIdFromEmployeeNumber tag, you can specify them in a thamtech\yaml\Parser configuration array that you pass to the Yaml::decode() method:

<?php
use thamtech\yaml\helpers\Yaml;

$data = Yaml::decode($yaml, [
    'on lookupIdFromEmployeeNumber' => function ($event) {
        // get the value associated with the `!lookupIdFromEmployeeNumber` tag
        $value = $event->value;
        
        // find the person's id and add it to the value
        $value['id'] = Employee::find()
            ->select(['id'])
            ->where(['employee_number' => $value['employee_number']])
            ->scalar();
        
        // set the updated value in the event; the value set in `value` will
        // replace the `TaggedValue` object in the parsed yaml data as long as we
        // mark that the event was handled
        $event->value = $value;
        $event->handled = true;
        
        // as a shortcut, the following is equivalent to the previous two lines:
        $event->handleValue($value);
    },
]);

print_r($data);
# Array
# (
#     [people] => Array
#         (
#           [john] => Array
#               (
#                   [id] => 1
#                   [name] => John
#               )
#           [bob] => yii\helpers\ReplaceArrayValue Object
#               (
#                   [value] => Array
#                       (
#                           [id] => 1001
#                           [name] => Bob
#                       )
#               )
#           [jane] => yii\helpers\UnsetArrayValue Object
#               (
#               )
#           [susan] => Array
#               (
#                   [employee_number] => 1234
#                   [name] => Susan
#                   [id] => 1004
#               )
#         )
# )

Encoding/Dumping

Example data to encode:

<?php
print_r($data);
# Array
# (
#     [people] => Array
#         (
#           [john] => Array
#               (
#                   [id] => 1
#                   [name] => John
#               )
#           [bob] => yii\helpers\ReplaceArrayValue Object
#               (
#                   [value] => Array
#                       (
#                           [id] => 1001
#                           [name] => Bob
#                       )
#               )
#           [jane] => yii\helpers\UnsetArrayValue Object
#               (
#               )
#           [susan] => Some\Package\EmployeeWithoutId Object
#               (
#                   [employee_number:Some\Package\EmployeeWithoutId:private] => 1234
#                   [name:Some\Package\EmployeeWithoutId:private] => Susan
#               )
#         )
# )

Dumping with the standard symfony/yaml library:

<?php
use Symfony\Component\Yaml\Yaml;

$yaml = Yaml::dump($data);
echo $yaml;
# people:
#     john:
#         id: 1
#         name: John
#     bob: null
#     jane: null
#     susan: null

Dumping with our default Yaml helper:

<?php
use Symfony\Component\Yaml\Yaml;

$yaml = Yaml::dump($data);
echo $yaml;
# people:
#     john:
#         id: 1
#         name: John
#     bob: !yii/helpers/ReplaceArrayValue
#         id: 1001
#         name: Bob
#     jane: !yii/helpers/UnsetArrayValue null
#     susan: null

In the example above, you can see that the keys tagged with !yii/helpers/ReplaceArrayValue and !yii/helpers/UnsetArrayValue were automatically encoded from the ReplaceArrayValue and UnsetArrayValue objects.

In order to add your own handlers for values like the EmployeeWithoutId object, you can specify them in a thamtech\yaml\Dumper configuration array that you pass to the Yaml::decode() method:

<?php
use thamtech\yaml\helpers\Yaml;
use Symfony\Component\Yaml\Tag\TaggedValue;

$yaml = Yaml::encode($data, [
    'on Some\Package\EmployeeWithoutId' => function ($event) {
        // get the EmployeeWithoutId object
        $value = $event->value;
        
        // decode the object into a TaggedValue object
        $event->value = new TaggedValue('lookupIdFromEmployeeNumber', [
            'employee_number' => $value->getEmployeeNumber(),
            'name' => $value->getName(),
        ]);
        $event->handled = true;
        
        // as a shortcut, the following is equivalent to setting `$event->value`
        // and setting `$event->handled = true`.
        $event->handleValue(
            new TaggedValue('lookupIdFromEmployeeNumber', [
                'employee_number' => $value->getEmployeeNumber(),
                'name' => $value->getName(),
            ])
        );
    },
]);

echo $yaml;
# people:
#     john:
#         id: 1
#         name: John
#     bob: !yii/helpers/ReplaceArrayValue/
#         id: 1001
#         name: Bob
#     jane: !yii/helpers/UnsetArrayValue null
#     susan: !lookupIdFromEmployeeNumber
#         employee_number: 1234
#         name: Susan

Configuring Default Handlers

You may not want to have to send a configuration array with one or more custom handlers every time you call Yaml::encode() or Yaml::decode(). The Yaml component sets its default Parser and Dumper definitions into Yii::$container. You can set your own default Parser and Dumper definitions there with your own handlers.

To not use any handlers by default (not even the Yaml component's default handlers):

<?php
use Yii;

Yii::$container->set('thamtech\yaml\Parser');
Yii::$container->set('thamtech\yaml\Dumper');

// alternatively, in your application configuration:
[
    'container' => [
        'definitions' => [
            'thamtech\yaml\Parser' => [],
            'thamtech\yaml\Dumper' => [],
        ],
    ],
];

However, it is probably more likely that you will want to start with the Yaml component's default handlers and override them or add your own. The Yaml::getDumperDefinition() and Yaml::getParserDefinition() methods are a convenient way to get Parser and Dumper definitions ready for setting in the Yii::$container.

use Yii;
use thamtech\yaml\helpers\Yaml;

Yii::$container->setDefinitions([
    'thamtech\yaml\Parser' => Yaml::getParserDefinition([
        // example: we are calling getParserDefinition() to use `Yaml`'s default
        // definitions as a base, but these lines shows how we can alter those
        // default definitions. In this case, we remove the 'ReplaceArrayValue'
        // and 'UnsetArrayValue' handlers by unsetting their array keys:
        'on yii/helpers/ReplaceArrayValue' => new \yii\helpers\UnsetArrayValue(),
        'on yii/helpers/UnsetArrayValue' => new \yii\helpers\UnsetArrayValue(),
        
        // example: adding your own handler
        'on lookupIdFromEmployeeNumber' => function ($event) {
            // get the value associated with the `!lookupIdFromEmployeeNumber` tag
            $value = $event->value;
            
            // find the person's id and add it to the value
            $value['id'] = Employee::find()
                ->select(['id'])
                ->where(['employee_number' => $value['employee_number']])
                ->scalar();
            
            // set the updated value in the event; the value set in `value` will
            // replace the `TaggedValue` object in the parsed yaml data as long as we
            // mark that the event was handled
            $event->value = $value;
            $event->handled = true;
            
            // as a shortcut, the following is equivalent to the previous two lines:
            $event->handleValue($value);
        },
    ]),
    'thamtech\yaml\Dumper' => Yaml::getDumperDefinition([
        // example: we are calling getDumperDefinition() to use `Yaml`'s default
        // definitions as a base, but these lines shows how we can alter those
        // default definitions. In this case, we remove the 'ReplaceArrayValue'
        // and 'UnsetArrayValue' handlers by unsetting their array keys:
        'on yii/helpers/ReplaceArrayValue' => new \yii\helpers\UnsetArrayValue(),
        'on yii/helpers/UnsetArrayValue' => new \yii\helpers\UnsetArrayValue(),
        
        // example: adding your own handler
        'on Some\Package\EmployeeWithoutId' => function ($event) {
            // get the EmployeeWithoutId object
            $value = $event->value;
            
            // decode the object into a TaggedValue object
            $event->value = new TaggedValue('lookupIdFromEmployeeNumber', [
                'employee_number' => $value->getEmployeeNumber(),
                'name' => $value->getName(),
            ]);
            $event->handled = true;
            
            // as a shortcut, the following is equivalent to setting `$event->value`
            // and setting `$event->handled = true`.
            $event->handleValue(
                new TaggedValue('lookupIdFromEmployeeNumber', [
                    'employee_number' => $value->getEmployeeNumber(),
                    'name' => $value->getName(),
                ])
            );
        },
    ]),
]);


// alternatively, in your application configuration:
[
    'container' => [
        'definitions' => [
            'thamtech\yaml\Parser' => Yaml::getParserDefinition([
                // ...
            ]),
            'thamtech\yaml\Dumper' => Yaml::getDumperDefinition([
                // ...
            ]),
        ],
    ],
];

Yaml Response Formatter

You can add the YamlResponseFormatter as a yaml formatter in your yii\web\Response component to add support for returning yaml responses.

<?php
// ... config ...
return [
    'response' => [
        'formatters' => [
            'yaml' => [
                'class' => 'thamtech\yaml\web\YamlResponseFormatter',
                
                // you can define your own dumper config like the earlier
                // examples:
                'dumper' => [
                    // `'class' => 'thamtech\yaml\Dumper'` is assumed, but can
                    // be overridden if you extend 'thamtech\yaml\Dumper'
                    
                    'on Some\Package\EmployeeWithoutId' => function ($event) {
                        // ... handle event, see earlier example ...
                    },
                ],
            ],
        ],
    ],
];

See Also