Download the PHP package didix16/php-apidatamapper without Composer
On this page you can find all versions of the php package didix16/php-apidatamapper. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download didix16/php-apidatamapper
More information about didix16/php-apidatamapper
Files in didix16/php-apidatamapper
Package php-apidatamapper
Short Description An extensible DTO library that allows map incoming API data to any of your entities/models by using a simple filed mapping language with filters and functions.
License MIT
Informations about the package php-apidatamapper
PHP API DataMapper
An extensible DTO library that allows map incoming API data to any of your entities/models by using a simple filed mapping language with filters and functions.
Content
- What is an API DataMapper
- What is a Model Map
- Installation
-
Usage
- Field Language
- FieldInterpreter
- FieldFilter
- FieldFunction
- ModelMap
- ModelMapFunction
- ModelMapFactory
- ApiDataObject
- ApiDataMapper
- Examples
What is an API DataMapper
An API DataMapper is a class that allows to map incoming data to a model or entity class you have without much effort. The only have to do is instruct which incoming fields should be mapped to you entity class fields and how. In other words, to allows to use the DTO pattern with every class you have.
It is able to preprocess data and transform it before the final data is set to an entity field.
What is a Model Map
So a model map is a class that handle all the tough stuff of mapping an incoming data into a model of your application. It has the configuration of which fields should be mappend into which ones and how. This is where all DTO magic happens. It uses a FieldInterpreter to archive this task.
Installation
Usage
In the following list you will see how to use each important part of this package:
Field language
This is the language that is being used by a model mapper. It syntax is very easy to understand: it allows to select which field do you want to extract and process from an APIDataObject.
The syntax allows to select a single field from an object, a field from a list (array, vector, whatever..)and transform incoming data ( i.e string to PHP DateTime). Also allows the use of aggregate functions for list items. For example, get the Maximum value of certain amount inside a list.
SYNTAX
Supose we have this JSON $data
Select first lvl field: warrior
Select specific field from object and apply a filter ( in this case BooleanFilter)
There is "no limit" on depth to select a deepest property.
We can stack filters as much as we need.
For example:
NOTE: those filter does not exists, you have to register them. In sections below you will see how.
The transformation pipeline order follows the specified in syntax, from left to right. In the example above, capitalize filter will be executed first, then snakecase, and so on...
Now, supose we have this JSON $data
Select a field inside a list:
Use an aggregate function inside a list:
NOTE: At the moment only MAX and MIN are available. However, you can extend the field language by adding the functions you need like: SUM, AVG, MEAN, etc...
BTW: You can only use aggregate functions with list fields inside! I.e: MAX(name) won't work, but MAX(warrior_list[].name) yep.
FieldInterpreter
As we saw above, to make FieldInterpreter do its work, we need a FieldParser.
The FieldLexer receives the Field Language input syntax. The FieldParser receives the FieldLexer as the only argument.
Finally you have to pass the FieldParser to FieldInterpreter and an APIDataObject instance to make its magic :).
If for some reason the syntax is invalid, you will get an Exception.
Finally, you need to call the #run method from the interpreter. The result will be an associative array where the key is the processed input and value the final processed data.
NOTE: If you specify a field which is not coming in incoming data, then the value for that field will be:
So you must check with ApiDataObject::isUndefined($res[$field]) if the result is correct or is an undefined field.
FieldFilter
As we saw, filters allows to pipeline data and transform it. We can add filters as much as we need.
By default there are two filters: DateFilter and BooleanFilter.
BooleanFilter transforms potential values to be casted as boolean value. For example: "yes", 1, "1", "true", true, "True", "tRUE", and so on... will be transformed to PHP true value. However, "no", 0, "1", "false", false, "FALSE", "fAlse" and so on... will be transformed into PHP false value.
You can instante a new BooleanFilter with an associative array that tells the filter which values should be treat as true and which as false:
DateFilter allows to parse any standard date formats into a PHP DateTime class.
First of all, it will try to transform by testing each of these formats:
If none of them were found, then $fromFormat='Y-m-d' constructor option will be used as last chance.
Also you can pass a timezone as a second optional argument
Feel free to extend both if you need.
To make your own filter you need to extend from:
Finally, to register a filter you need to call #loadFilter(FieldFilter $filter) method from FieldInterpreter BEFORE call #run method
IMPORTANT: filters name MUST BE the same in field language syntax. If you named "capitalize" your filter, then in $input syntax, the filter should be "capitalize" as well.
FieldFilter receive as mandatory parameter its name. If you look inside BooleanFilter and DateFilter, you will see that there is a
and
lines respectively inside their constructors
FieldFunction
Like FieldFilter but for AggregateFunctions. As I wrote before, aggregate functions only works with lists, so be carefully.
There are MAX and MIN functions ( self explanatory).
If you need to add your own functions, you have to extend
Finally, to register a function you need to call #loadFunction(InterpreterFunction $function) method from FieldInterpreter BEFORE call #run method
IMPORTANT: function name MUST BE the same in field language syntax. If you named "AVG" your function, then in $input syntax, the filter should be "AVG" as well.
ModelMap
As I explained before, the model map is the key of the api data mapper. It handles all the ugly tasks to parse and manage data.
Fortunately, the only thing we have to do is tell to a model map how we need to map the fields and if we need to preprocess and postproces them.
For example, imagine we have these entity classes:
And imagine we have this source of data:
How we should map that data into our Monster entity?
Without a data mapper, we will probably design an specific DTO or similar. Maybe some people hardcode transformation ( yeah I see a lot of things in this live), whatever.
But what if I told you that only you need is a mapping configuration (and maybe a model map factory)?
WOW, that's a lot information! Yeah I know. But for the moment pay attention to MonsterModelMap and look mapFields method. It sounds familiar to you, right? Correct, the field language! As we can see, a model map uses the field interpreter to map the fields for us but also is able to execute a "post-parsing" function before set the final value to our entity. What does that means? Well, look at the color property of our monster.
It is not just an "ordinary" value like a string or number, is a class! Well it is true that we could made some filter that transforms a value into a class but, filters only have access to a single value. What happens if we need more data? Well the answer is the ModelMapFunction. This kind of function allows to us make last data transformations before set to our entity and also has access to the APIDataObject.
Remember the color property? Yeah, the color is a class, so, we can add a model map function that resolves a value and turns into our Color class. If we have complex classes that require more data that comes from API, we can access that data but also we can call any service from our app, like DDBB storage or something and do the things we have to do with it :).
ModelMapFunction
Model map functions are very easy to implement: just need the run(...$args) method.
$args 0 contains the value coming from interpreter (after filter pipeline)
$args 1 holds the entire APIDataObject.
NOTE: Data may not be the original one because the Interpreter may changed the value by applying your filters.
Finally how we tell to our model map that use this function?
Well that is simple. Remember this line?
Perfect! As you deducted, the key of the mapFields array is the input of the field language and the value must be the field of our entity but optinally it can be suffixed by a colon and a function name. Usually, the name of the function is the camel case of the name class without the MapFunction suffix, thus is why the class names of every MapFunction should be:
But that's not all, we have to register the function to our model map.
Well there is a #use() method that allows us to register not one but three kind of objects:
- ModelMapFunction
- AggregateFunction
- FieldFilter
This way we can extend our language field mapping though a model map instead of talking directly with the fieldparser.
ModelMapFactory
Why we need a factory to instnatiate model maps? Well this is a pice of the amazing ApiDataMapper that allows to pass a class of any kind ( the ones we have in our ModelMapFactory) and let configure our model maps in runtime as well as leave the tough work to ApiDataMapper.
Remember that our goal is only to configure the mapping and leave the rest to APIDataMapper :)
As we saw before, this is an example of a factory that can generate mappings for Warrior and Monster classes.
In the future I'll change this because no one wants to have a gigantic switch for each class :)
In the mean time, you can generate different model maps factories and have different api data mappers.
ApiDataObject
- See didix16/php-apidataobject - A simple library that allows easy handle incoming data from any sources (specially from API sources)
ApiDataMapper
And here we are, the API functionality usage of this marvelous package :)
Basically, from this packge we will need:
-
GlobalApiDataMapper (or ApiDataMapper if you need something else)
-
ModelMap
Basically you will make classes from this class by each of your entities.
For example, supose we have a Warrior entity. This could be a model map configuration
- ModelMapFactory
- FieldFilter
- AggregateFunction
-
ApiDataObject
Better explained with examples:
Feel free to instnatiate your api data object from any source and remember transform it to a valid php data.
For example you could read from XML and transform it to an array or an object. This could be done inside static function called fromXML():
You know what I mean, let fly your imagination ;)
Examples
You will find some examples at examples folder
You sould look at:
- index.php
- FieldInterpreter.php
They are ready to go, so if you open your terminal after installation:
OR
You should see a few results from the examples
Credits
Feel free to use this library but do not forget to mention that I'm the owner :).
Sorry for my bad english. I'll try to fix any grammar errors in the future.
Also feel free to send me an issue, report bugs, suggestions, etc.
All versions of php-apidatamapper with dependencies
didix16/php-interpreter Version ^1.0
didix16/php-hydrator Version ^1.0
didix16/php-grammar Version ^1.0
didix16/php-apidataobject Version >= 1.0.3