PHP code example of didix16 / php-apidatamapper

1. Go to this page and download the library: Download didix16/php-apidatamapper library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

didix16 / php-apidatamapper example snippets


composer 


use didix16\Api\ApiDataObject\ApiDataObject;

class MyApiDataObject extends ApiDataObject {}


$data = MyApiDataObject::fromJson($data);

$input = "warrior";


$fi = new FieldInterpreter($parser, $data);
$lexer = new FieldLexer($input);
$parser = new FieldParser($lexer);

$res = $fi->run();

var_dump($res);
/**
 * array(1) {
 *    ["warrior"]=> stdClass(warrior...)
 *    
 * }
 * 
 */



use didix16\Api\ApiDataObject\ApiDataObject;

class MyApiDataObject extends ApiDataObject {}

/**
 * Example of how to parse a field from api data and turn into a boolean
 */
$input = "warrior.active:boolean";
$lexer = new FieldLexer($input);
$parser = new FieldParser($lexer);

$data = MyApiDataObject::fromJson($data);

$fi = new FieldInterpreter($parser, $data);

$res = $fi->run();

var_dump($res);
/**
 * array(1) {
 *    ["warrior.active:boolean"]=>
 *    bool(false)
 * }
 * 
 */

$data = MyApiDataObject::fromJson($data);

$input = "warrior_list[].name";
$lexer = new FieldLexer($input);
$parser = new FieldParser($lexer);
$fi = new FieldInterpreter($parser, $data);

$res = $fi->run();

var_dump($res);
/**
 * array(1) {
 * ["warrior_list[].name"]=>
 * array(2) {
 *   [0]=>
 *   string(8) "Lancelot"
 *   [1]=>
 *   string(6) "Arthur"
 * }
 *}
 * 
 */

$input = "MAX(warrior_list[].kills)";
$lexer = new FieldLexer($input);
$parser = new FieldParser($lexer);
$fi = new FieldInterpreter($parser, $data);

$res = $fi->run();

var_dump($res);

/**
 * array(1) {
 * ["MAX(warrior_list[].kills)"]=>
 * int(90)
 *}
 */

$input = "MAX(warrior_list[].kills)";
$lexer = new FieldLexer($input);
$parser = new FieldParser($lexer);
$fi = new FieldInterpreter($parser, $data);

$res = $fi->run(); // returns an array

class didix16\Api\ApiDataObject\UndefinedField {}

// The second parameter is $forceFalse.
// If is true then if the value founded is not in the specified list nor is a php boolean value the value will be set to false as default.
// By default is false and thus will leave the value as is if is "non-booleable"
$filter = new BooleanFilter(
[
    "true" => [
        "done",
        "completed",
        ...
    ],
    "false" => [
        "pending",
        "not_finished",
        ...
    ]
], true);

DateTimeInterface::ATOM,
DateTimeInterface::COOKIE,
DateTimeInterface::ISO8601,
DateTimeInterface::RFC822,
DateTimeInterface::RFC850,
DateTimeInterface::RFC1036,
DateTimeInterface::RFC1123,
DateTimeInterface::RFC2822,
DateTimeInterface::RFC3339,
DateTimeInterface::RFC3339_EXTENDED,
DateTimeInterface::RSS,
DateTimeInterface::W3C

//                     $fromFormat   $toTimezone
$filter = new DateFilter('d-m-Y', 'Europe/London');

class  didix16\Api\ApiDataMapper\FieldInterpreter\Filters\FieldFilter;


/**
 * Custom FieldField: allows capitilize strings
 */
class CapitalizeFilter extends FieldFilter {

    protected function transform(&$value)
    {
        if($this->assertString($value))
            $value = strtoupper($value);
    }
}

/**
 * Custom FieldFilter: adds '_thisIsASuffix' as a string suffix
 */
class SuffixerFilter extends FieldFilter {

    protected function transform(&$value)
    {
        if($this->assertString($value))
            $value = $value . '_thisIsASuffix';
    }
}

$input = "warrior.name:capitalize,suffixer";
...

$fi = new FieldInterpreter($parser, $data);
$fi
    ->loadFilter(new CapitalizeFilter('capitalize'))
    ->loadFilter(new SuffixerFilter('suffixer'));

parent::__construct("boolean")

parent::__construct("date")

class  didix16\Api\ApiDataMapper\FieldInterpreter\Functions\AggregateFunction;


class AvgFunction extends AggregateFunction {

    public function __construct()
    {
        parent::__construct("AVG");
    }

    /**
     * Returns the average value within iterable $data
     * If $data is empty, then return null
     * @return mixed
     */
    protected function avg(){

        if (empty($this->iterable)) return null;

        if (!$this->field)
            return array_sum($this->iterable)/ count($this->iterable);
        else {

            $values = array_map(function($obj){
                return $obj->{$this->field} ?? null;
            }, $this->iterable );

            return array_sum($values) / count($values);

        }
    }

    /**
     * Given an interable, returns the avergage interpreted value
     * @param $args
     * @return mixed
     */
    public function run(...$args)
    {
        parent::run(...$args);
        return $this->avg();
    }
}

$input = "AVG(warrior_list[].kills)";
$lexer = new FieldLexer($input);
$parser = new FieldParser($lexer);
$fi = new FieldInterpreter($parser, $data);
$fi
    ->loadFunction(new AvgFunction());

$res = $fi->run();

var_dump($res);

/**
 * array(1) {
 * ["AVG(warrior_list[].kills)"]=>
 * float(48.333333333333)
 *}
 */


/**
 * An other ORM class or system class that is being used by another class as a property
 */
class Color {

    protected string $name;

    public function __construct(string $color)
    {
        $this->name = $color;
    }

    public static function fromName(string $color): Color {

        return new static($color);
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function __toString()
    {
        return '<Color(' .$this->getName(). ')>';
    }
}

/**
 * A potential ORM entity.
 */
class Monster {

    protected string $name;

    protected Color $color;

    protected bool $eatHumans;

    protected int $numLegs;

    public function setName($name): Monster
    {
        $this->name = $name;
        return $this;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setColor($color): Monster
    {
        $this->color = $color;
        return $this;
    }

    public function getColor(): Color
    {
        return $this->color;
    }

    public function setEatHumans($flag): Monster
    {
        $this->eatHumans = $flag;
        return $this;
    }

    public function eatsHumans(): bool
    {
        return $this->eatHumans;
    }

    public function setNumLegs($legs): Monster
    {
        $this->numLegs = $legs;
        return $this;
    }

    public function getNumLegs(): int
    {
        return $this->numLegs;
    }
}


 // https://a-monster-api.com/api/monster/Blob
$jsonIncomingFromMonsterAPI = <<<JSON
{
    "monster": {
        "name": "Blob",
        "eat_humans": 0,
        "color": "green",
        "num_legs": 0
    }
}
JSON;

use didix16\Api\ApiDataMapper\ModelMapFactoryInterface;
use didix16\Api\ApiDataMapper\ModelMapInterface;

class ModelMapFactory implements ModelMapFactoryInterface
{
    public static function build($modelClass): ModelMapInterface
    {
        switch($modelClass){
            case Warrior::class:
                return new WarriorModelMap();
            case Monster::class:
                    return new MonsterModelMap();
            default:
                throw new \Exception(sprintf('There are not factory for class %s', $modelClass));
        }
    }
}


use didix16\Api\ApiDataMapper\ModelMap;

class MonsterModelMap extends ModelMap
{
    public function __construct()
    {
        parent::__construct();
        $this
            // configured single fields
            ->mapFields([
                'monster.name'              => 'name',
                'monster.color'             => 'color:getColor',
                'monster.eat_humans:boolean'=> 'eatHumans',
                'monster.num_legs'          => 'numLegs'
            ]);
    }
}

$apiData = MonsterApiDataObject::fromJson($jsonIncomingFromMonsterAPI);

/**
 * @var Monster $monster
 */
$monster = $apiDataMapper
    ->configure(Monster::class)
    ->use([
        new GetColorMapFunction()
    ])
    ->mapper
    ->mapToModel(Monster::class, $apiData);

echo "\n";
echo "\n";
echo 'Name: ' . $monster->getName() . "\n";
echo 'Eat Humans: ' . ($monster->eatsHumans() ? 'yes' : 'no') . "\n";
echo 'Color: ' . $monster->getColor() . "\n";
echo 'Number of legs: ' . $monster->getNumLegs() . "\n";
echo '========================'. "\n";

use didix16\Api\ApiDataMapper\ModelMapFunction;

class GetColorMapFunction extends ModelMapFunction
{
    // parameters and its default values
    protected $parameterName = null;

    public function run(...$args)
    {
        $colorName = $args[0];
        $apiDataObject = $args[1];
        $fieldName = $args[2];

        /**
         * At v1.0.5+ also you can pass external parameters to be used inside run method at construction time
         * 
         * Remember that the given parameters should exists in your ModelMapFunction
         * 
         * You can build ModelMapFunction using:
         * 
         * new YourModelMapFunction("", ['param1' => 'value1', ...])
         * YourModelMapFunction::withParameters(['param1' => 'value1', ...]) <== this is an alias of constructor above
         * 
         * Example:
         * 
         * GetColorMapFunction::withParameters(['parameterName' => '#FF0000'])
         * 
         */
        $colorString = $this->parameterName; // #FF0000

        return new Color($colorName);
    }
}

'monster.color' => 'color:getColor',

use didix16\Api\ApiDataMapper\ModelMapFactoryInterface;
use didix16\Api\ApiDataMapper\ModelMapInterface;

class ModelMapFactory implements ModelMapFactoryInterface
{
    public static function build($modelClass): ModelMapInterface
    {
        switch($modelClass){
            case Warrior::class:
                return new WarriorModelMap();
            case Monster::class:
                    return new MonsterModelMap();
            default:
                throw new \Exception(sprintf('There are not factory for class %s', $modelClass));
        }
    }
}
 
        /**
         * Given A model class and an ApiDataObject, attempt to generate an instance of $modelClass with data given
        * @param $modelClass - Should be any kind of ORM entity or object class representing a model in DDBB
        * @param ApiDataObjectInterface $data
        * @return object
        * @throws ApiDataMapperException
        */
        public function mapToModel($modelClass, ApiDataObjectInterface $data): object
        
 
        /**
         * Given a model class and ApiDataObjectInterface, attempts to generate an interable of
        * $modelClass with data given
        * @param $modelClass - Should be any kind of ORM entity or object class representing a model in DDBB
        * @param ApiDataObjectInterface $data
        * @return iterable
        * @throws ApiDataMapperException
        */
        public function mapToModelList($modelClass, ApiDataObjectInterface $data): iterable
        
 
        /**
         * Given an instance of a model and an ApiDataObjectInterface, attempt to refresh the model with data given
        * @param object $instance
        * @param ApiDataObjectInterface $data
        * @throws ApiDataMapperException
        */
        public function refreshModel(object $instance, ApiDataObjectInterface $data): void
        

        /**
         * Refreshes instance $to using instance $from
        * If $strict is true and the instances are not the same class then an exception is thrown
        * @param object $to,
        * @param object $from
        * @param bool $strict
        * @throws ApiDataMapperException
        */
        public function refreshModelFromOtherModel(object $to, object $from, bool $strict = false): void
        

        /**
         * Tell to this model map that should generate multiple instances by using $arrayField as field list
        * @param string $arrayField
        * @return ModelMap
        */
        public function setMultiple(string $arrayField): self
        

        /**
         * Tell to this model map that don't generate multiple instances.
        * This methods unset the arrayField if was stablished using #setMultiple method
        */
        public function unsetMultiple(): self
        

        /**
         * Check if this model map is configured to process and return a multiple instances
        */
        public function isMultiple(): bool
        

        /**
         * Given an associative array with key as externalField and a value as modelField,
        * tries to make the association for this model map
        * @param array $fieldMap
        * @return $this
        * @throws Exception
        */
        public function mapFields(array $fieldMap): self
        

        /**
         * Same as mapFields but for list fields
        */
        public function mapListFields(array $listFieldMap): self
        

        /**
        * Given a model field names, ignores the fields on field interpreting process if model instance
        * field has value different from null or empty
        * @param iterable|string $fields
        * @return $this
        */
        public function ignoreFieldsIfSet($fields): ModelMap
        

        /**
         * Given model fields, remove from ignore field list, the specified model fields
         * @param $fields
         * @return $this
         */
         public function unignoreFieldsIfSet($fields): ModelMap
        

        /**
         * Allows to register external components to extends the functionality of the model map language
        * The components allowed are: FieldFilter, AggregateFunction and ModelMapFunction
        * @param FieldFilter|FieldFilter[]|AggregateFunction|AggregateFunction[]|ModelMapFunction|ModelMapFunction[] $components
        */
        public function use($components): self
        

        /**
         * 
         * ACCESSIBLE ONLY FROM GlobalApiDataMapper. It uses HasModelMapFactory Trait.
         * 
         * Allows to access the model map for specified $modelClass
        * Returns a HighOrderModelMapConfiguration to allow chain access
        * between model map and api data mapper
        * @param string $modelClass
        * @return HighOrderModelMapConfiguration
        */
        public function configure($modelClass): HighOrderModelMapConfiguration
        

    use didix16\Api\ApiDataMapper\ModelMap;

    class WarriorModelMap extends ModelMap
    {
        public function __construct()
        {
            parent::__construct();
            $this
                // configured single fields
                ->mapFields([
                    'warrior.name'              => 'name',
                    'warrior.is_active:boolean' => 'active',
                    'warrior.weapon'            => 'weapon',
                    'warrior.comes_from'        => 'place'
                ])
                // configuring map list fields when coming from a list but not using #setMultiple() method here
                ->mapListFields([
                    'name'              => 'name',
                    'is_active:boolean' => 'active',
                    'weapon'            => 'weapon'
                ]);
        }
    }
    

        /**
         * Given a $modelClass, returns a new instance of a ModelMapInterface
        * that is related to the $modelClass
        */
        public static function build($modelClass): ModelMapInterface
        


        # EXTENDS THIS CLASS

        /**
         * The name this filter has
         * Must be the same on syntax field language
         */
        public function __construct($name);
        

        /**
         * Gets a $value and transform it into something else
         */
        protected function transform(&$value);
        


        # EXTENDS THIS CLASS

        /**
         * The list you want to iterate
         */
        protected $iterable = [];
        /**
         * The field name from every object inside the list. If null means we only
         * iterate over list elements,
         * else each elem shuld be an object
         */
        protected $field = null;
        
        /**
         * Do this allways!
         */
        protected function run(...$args){

            parent::run($args);
            // do whatever you want from here
        }
        


    class ApiPlatformDataObject extends ApiDataObject {}

    $json = <<<JSON
    {
        "property1": "value1",
        "property2": "value2",
        ...
    }
    JSON;

    $apiData = ApiPlatformDataObject::fromJson($json);

        /**
         * Different accessors
         */
        $apiData['property1'];
        $apiData->property1;
        $apiData->property1();

        /**
         * Different setters
         */
        $apiData['property1'] = 'value5';
        $apiData->property1 = 'value5';

        // chainable setter properties
        $apiData
        ->property1('value5')
        ->property2('value6')
        ...
    

    $data = [
        'property1' => 'value1',
        'property2' => 'value2',
        ...
    ];

    /**
     * Instantiate from an array
     */
    $apiData = new ApiPlatformDataObject($data);

    $data = (object)[
        'property1' => (object)[
            'objProp1' => 1,
            'objProp2' => 2,
            ...
        ],
        'property2' => 'value2',
        ...
    ];

    /**
     * Instantiate from an object
     */
    $apiData = new ApiPlatformDataObject($data);
    


    class MyXMLApiDataObject extends ApiDataObject {

        public static fromXML($xml): MyXMLApiDataObject
        {
            // parse your XML
            // ... or whatever ...
            $list = $xmlParsed;
            return new static($list);
        }
    }
    
sh
php vendor/didix16/php-apidatamapper/examples/index.php
sh
php vendor/didix16/php-apidatamapper/examples/FieldInterpreter.php