Skip to content
comhon-project edited this page Jun 18, 2020 · 16 revisions

Table of contents

  1. Preamble
  2. Dataset
  3. Simple object request
  4. Complex objects request
    1. Intermediate objects request
    2. Advanced objects request
    3. Order, offset and limit
    4. Count
  5. Load Foreign values

Preamble

Comhon! request api permit to retrieve any object that have an associated manifest. It allow to build complex requestes in unique Comhon! request format and it will transform requestes in language understandable by your "serialization system" (for example sql database).
First, the request must be built, and second, it must be executed. The execution will return a ComhonObject (for simple requestes) or a ComhonArray (for complex requestes).
If any error appears, for example a malformed request, a ComhonException will be thrown (All error codes are listed in class Comhon\Exception\ConstantException).

Dataset

To have a better comprehension we will define some manifest and serialized object

Person

Manifest

<manifest version="2.0">
    <properties>
        <property type="integer" is_id="1" name="id"/>
        <property type="string" name="firstName"/>
        <property type="string" name="lastName"/>
        <property is_foreign="1" type="\Test\Place" name="birthPlace"/>
        <property is_foreign="1" type="\Test\Man" name="father"/>
        <property is_foreign="1" type="\Test\Woman" name="mother"/>
        <property is_foreign="1" type="array" name="children">
            <values type="\Test\Person" name="child"/>
        </property>
        <property is_foreign="1" type="array" name="houses">
            <values type="\Test\House" name="house"/>
        </property>
    </properties>
</manifest>

Serialization

<manifest version="2.0">
    <serialization type="sqlTable" id="person"/>
    <properties>
        <birthPlace serializationName="birth_place_id"/>
        <father serializationName="father_id"/>
        <mother serializationName="mother_id"/>
        <children>
            <aggregations>
                <aggregation>mother</aggregation>
                <aggregation>father</aggregation>
            </aggregations>
        </children>
        <houses>
            <aggregations>
                <aggregation>owner</aggregation>
            </aggregations>
        </houses>
    </properties>
</manifest>

Serialized object

id firstName lastName birthPlace_id father_id mother_id
1 john doe 1
2 jane doe 2
3 marie doe 3 1 2
4 philippe doe 3 1 2
5 emilie doe 2 1
6 walter doe 2 5
7 jesse doe 2 5

House

Manifest

<manifest>
    <properties>
        <property type="integer" name="id" is_id="1"/>
        <property type="float" name="surface"/>
        <property type="boolean" name="garden"/>
        <property type="person" name="owner" is_foreign="1"/>
    </properties>
</manifest>

Serialization

<manifest>
    <serialization type="sqlTable" id="house"/>
    <properties>
        <owner serializationName="owner_id"/>
    </properties>
</manifest>

Serialized object

id surface garden owner_id
1 110 false 1
2 130 true 2
3 120 true 2

Place

Manifest

<manifest>
    <properties>
        <property type="integer" is_id="1" name="id"/>
        <property type="integer" name="number"/>
        <property type="string" name="type"/>
        <property type="string" name="name"/>
        <property type="string" name="town"/>
    </properties>
</manifest>

Serialization

<manifest>
    <serialization type="sqlTable" id="place"/>
    <properties/>
</manifest>

Serialized object

id number type name town
1 16 street main street New York
2 3 street second street New York
3 10 avenue Jean Moulin Paris

Simple object request

Simple object request permit to retrieve one object by its model name and its id. The entrypoint is Comhon\Request\SimpleLoadRequest. It manage all types of serialization. You cannot use it if your model doesn't have id property.

$params        = new stdClass();
$params->model = 'Test\Person';
$params->id    = 1;

$request = SimpleLoadRequest::buildObjectLoadRequest($params);
$result = $request->execute(); // return ComhonObject or null if not found

If your model has several id properties you must specify them in a json encoded array, and order of values must be the same as order of id properties in manifest.

$params        = new stdClass();
$params->model = 'Test\MyModel';
$params->id    = '[1,"id2",456]';

$request = SimpleLoadRequest::buildObjectLoadRequest($params);
$result = $request->execute(); // return ComhonObject or null if not found

Complex objects request

The entrypoint to build and execute complex requetes is Comhon\Request\ComplexLoadRequest.

$request = ComplexLoadRequest::buildObjectLoadRequest($params);
$result = $request->execute(); // return ComhonArray

The parameters of request will determine if request is an intermediate or advanced request.

Intermediate objects request

Intermediate objects request permit to retrieve several objects that match with some given filters. Only objects with an sql database serialization (sqlTable) can be requested, and models specified in filter must be linked to an sql database serialization too.

Use case 1

States

I want houses
-that have a surface supperior than 90 m“ or with a garden
-and that have an owner called marie or jane, and born in second street New York

Request

{
    "model" : "Test\\House",
    "filter" : {
        "type" : "conjunction",
        "elements" : [
            {
                "type" : "disjunction",
                "elements" : [
                    {
                        "model"    : "Test\\House",
                        "property" : "surface",
                        "operator" : ">",
                        "value"    : 90
                    },
                    {
                        "model"    : "Test\\House",
                        "property" : "garden",
                        "operator" : "=",
                        "value"    : true
                    }
                ]
            },
            {
                "model"    : "Test\\Person",
                "property" : "firstname",
                "operator" : "=",
                "value"    : ["marie","jane"]
            },
            {
                "model"    : "Test\\Place",
                "property" : "name",
                "operator" : "=",
                "value"    : "second street"
            },
            {
                "model"    : "Test\\Place",
                "property" : "town",
                "operator" : "=",
                "value"    : "New York"
            }
        ]
    }
}

Result

Stringified ComhonArray :

[
    {
        "id": 2,
        "surface": 130,
        "garden": true,
        "owner": 2
    },
    {
        "id": 3,
        "surface": 120,
        "garden": true,
        "owner": 2
    }
]

Explanations

In intermediate request you don't have to specify relations between differents models, Comhon! api will find them automatically (relations between models permit to construct sql joins). Your request must have :

  • a model : the requested objects model name
  • a filter that is a literal or a clause : the filter to apply
Literal

A literal represent a condition in data base request, something like WHERE (foo = 'bar').
In intermediate request, literal must have following properties :

  • model
  • property
  • operator (managed operators : =,<>,<,>,<=,>=)
  • value
Clause

A clause is a group of several literals and/or sub clauses, something like WHERE ... ((foo = 'bar') AND (bar = 'foo')).
The type determine if elements in clause are linked by AND or OR

  • disjonction : OR
  • conjonction : AND
Logical Form

the logical form of previous request would be : (a ∨ b) ∧ c ∧ d ∧ e

Use case 2

States

I want persons
-named Paul or john
-and which have between 2 and 6 grandchildren

Request

{
    "model" : "Test\\Person",
    "filter" : {
    "type" : "conjunction",
        "elements" : [
            {
                "model"    : "Test\\Person",
                "property" : "firstName",
                "operator" : "=",
                "value"    : ["Paul", "john"]
            },
            {
                "model"     : "Test\\Person",
                "queue"     : {
                    "property" : "children", 
                    "child" : {
                        "property" : "children"
                    }
                },
                "having" : {
                    "type" : "conjunction",
                    "elements" : [
                        {
                            "function" : "COUNT",
                            "operator" : ">",
                            "value"    : 2
                        },
                        {
                            "function" : "COUNT",
                            "operator" : "<",
                            "value"    : 6
                        }
                    ]
                }
            }
        ]
    }
}

Result

Stringified ComhonArray :

[
    {
        "id": 1,
        "firstName": "john",
        "lastName": "doe",
        "birthPlace": "1"
    }
]

Explanations

A literal can contain "having literal(s)" instead of a simple description of condition (property, operator, value). To describe it, this kind of literal must have :

  • a queue
  • a having that is a having literal or a having clause
Queue

A queue permit to know on which node you want to apply your having condition(s). The queue is an imbrication of several objects describing : a property (property) and its possible child (child)

Having clause

A having clause is like a simple clause but can only contain having literals and having clauses.

Having literal

A having literal represent a condition in data base request, something like HAVING ... COUNT(foo) = bar
Having literal must have following properties :

  • function (managed function : SUM,AVG,MAX,MIN,COUNT)
  • property (except for COUNT function)
  • operator (managed operators : =,<>,<,>,<=,>=)
  • value

Limitations

  • Imagine we want to add a property address to model Test\House and this property would have type Test\Place (same type as property birthPlace in model Test\Person). Now we want a person born in certain place or a person that have a house in certain place. But this request case is impossible to resolve in intermediate request because we are not able to determine on which property apply the literal (literal only describe model). Fortunately we can manage this problem with advanced request.
  • sql request building via Intermadiate request can take more time than via advanced request. However that can be negligible compared sql query execution time. It depend on your database size and complexity.

Advanced objects request

Advanced objects request permit to retrieve several objects that match with some given filters. Only objects with an sql database serialization (sqlTable) can be requested, and properties specified in tree must be linked to an sql database serialization too.
In advanced objects request you must define relations between properties linked to a literal. These relations are display in a graph (actually in a tree) which each node is a property except root node that is your requested model object. And each literal refer to a node of this graph.

Use case 1

States

I want persons
-that have a grandson named walter
-and that have a house without garden

Request

{
    "tree" : {
        "model"   : "Test\\Person",
        "id"      : "p1",
        "children" : [
            {
                "property" : "houses",
                "id"       : "houses"
            },
            {
                "property" : "children",
                "id"       : "p2",
                "children"  : [
                    {
                        "property" : "children",
                        "id"       : "p3"
                    }
                ]
            }
        ]
    },
    "filter" : {
        "type" : "conjunction",
        "elements" : [
            {
                "node"     : "p3",
                "property" : "firstName",
                "operator" : "=",
                "value"    : "walter"
            },
            {
                "node"     : "houses",
                "property" : "garden",
                "operator" : "=",
                "value"    : false
            }
        ]
    }
}

Explanations

The tree
visualization

To have a better compehension here is the tree in more visual way :

                  person (p1)
           ___________|__________
          |                      |
    children (p2)          houses (houses)
          |
    children (p3)
node

Each node tree has :

  • a property property or model
    • root node contain model that is the model name of requested objects.
    • other nodes contain property that is a property name of the model of the parent node.
  • a property id that identify current node (must be unique).
  • a property children (optional) that contain children properties of current model node

Note that in given example, you must differentiate property children of a node and the property name children of the model Test\Person.

Literal

As we can see in request, each node in tree has an id. Literals refer to this node id instead of model name like it's done in intermediate objects request. This permit to know exactly on which node we want to apply literal.

Order, offset and limit

  • We can order returned objects. Order can be applied on one or several properties of requested model. We can order in ascendant or descendant way (ASC or DESC).
  • We can apply a limit of returned objects
  • We can apply an offset to return objects from a certain range

To apply limit or offset we must specify order too. Actually if order is not specified, two same objects requests can return objects in different order, so returned objects may not be the same with a limit.

Example : I want five persons maximum, from third person, order by their firstname

{
    "model" : "Test\\Person",
    "limit" : 5,
    "offset" : 3,
    "order" : [{"property":"firstName", "type":"ASC"}]
}

Count

Instead of retrieve objects, it is possible to count objects that match built request. count function return the global count of object and ignore limit and offset even if they are set on built request. This function is usefull for pagination.

$request = ComplexLoadRequest::buildObjectLoadRequest($params);
$result = $request->count(); // return integer

It is possible to call count and execute on same built request.

$request = ComplexLoadRequest::buildObjectLoadRequest($params);
$collection = $request->execute(); // return ComhonArray
$count = $request->count(); // return integer

Load foreign values

By default, only requested objects are loaded, Aggregations and foreign values are not loaded. For example if you request a Person, mother value (foreign value) will have only id, and children (aggregation) will not be set.

There are two settings that you can add to your request parameters to load these values :

  • requestChildren (boolean)
  • loadForeignProperties (boolean)

There are several possible configurations :

  • loadForeignProperties and requestChildren are set to false. This is the default case explained before.
  • loadForeignProperties and requestChildren are set to true. Foreign values and aggregations are fully loaded.
  • loadForeignProperties is set to true and requestChildren is set to false. Foreign values are loaded and aggregations are not.
  • loadForeignProperties is set to false and requestChildren is set to true. Foreign values are not loaded and only aggregations ids are loaded.

Clone this wiki locally