-
Notifications
You must be signed in to change notification settings - Fork 0
Requester
Requester permit to retrieve any comhon object with a serialization set. It allow to build complex requests in unique Comhon! request format and it will transform requests 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 requests) or a ComhonArray (for complex requests).
Requests might be executed in public or private context. In public context, some settings might be not available to prevent requests too complex with too long execution time.
If any error appears, for example a malformed request, a ComhonException will be thrown (All error codes are listed in class Comhon\Exception\ConstantException).
To have a better comprehension we will define some manifest and serialized object
<root name="Test\Person" version="3.0">
<properties>
<property name="id" is_id="1" auto="incremental" __inheritance__="Comhon\Manifest\Property\Index"/>
<property name="firstName" __inheritance__="Comhon\Manifest\Property\String"/>
<property name="lastName" __inheritance__="Comhon\Manifest\Property\String"/>
<property name="birthPlace" model="\Test\Place" is_foreign="1" __inheritance__="Comhon\Manifest\Property\Object"/>
<property name="father" model="\Test\Person\Man" is_foreign="1" __inheritance__="Comhon\Manifest\Property\Object"/>
<property name="mother" model="\Test\Person\Woman" is_foreign="1" __inheritance__="Comhon\Manifest\Property\Object"/>
<property name="children" __inheritance__="Comhon\Manifest\Property\Aggregation">
<values name="child" model="\Test\Person"/>
<aggregations>
<aggregation>mother</aggregation>
<aggregation>father</aggregation>
</aggregations>
</property>
<property name="houses" __inheritance__="Comhon\Manifest\Property\Aggregation">
<values name="house" model="\Test\House"/>
<aggregations>
<aggregation>owner</aggregation>
</aggregations>
</property>
</properties>
</root><root name="Test\Person" version="3.0">
<serialization>
<foreign_settings id="person" __inheritance__="Comhon\SqlTable"/>
</serialization>
<properties>
<firstName serialization_name="first_name"/>
<lastName serialization_name="last_name"/>
<birthPlace serialization_name="birth_place_id"/>
<father serialization_name="father_id"/>
<mother serialization_name="mother_id"/>
</properties>
</root>| id | first_name | last_name | birth_place_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 |
<root name="Test\House" version="3.0">
<properties>
<property name="id" is_id="1" auto="incremental" __inheritance__="Comhon\Manifest\Property\Index"/>
<property name="surface" __inheritance__="Comhon\Manifest\Property\Float"/>
<property name="garden" __inheritance__="Comhon\Manifest\Property\Boolean"/>
<property name="owner" model="\Test\Person" is_foreign="1" __inheritance__="Comhon\Manifest\Property\Object"/>
</properties>
</root><root name="Test\House" version="3.0">
<serialization>
<foreign_settings id="house" __inheritance__="Comhon\SqlTable"/>
</serialization>
<properties>
<owner serialization_name="owner_id"/>
</properties>
</root>| id | surface | garden | owner_id |
|---|---|---|---|
| 1 | 110 | false | 1 |
| 2 | 130 | true | 2 |
| 3 | 120 | true | 2 |
<root name="Test\Place" version="3.0">
<properties>
<property name="id" is_id="1" auto="incremental" __inheritance__="Comhon\Manifest\Property\Index"/>
<property name="number" __inheritance__="Comhon\Manifest\Property\Integer"/>
<property name="type" __inheritance__="Comhon\Manifest\Property\String"/>
<property name="name" __inheritance__="Comhon\Manifest\Property\String"/>
<property name="town" __inheritance__="Comhon\Manifest\Property\String"/>
</properties>
</root><root name="Test\Place" version="3.0">
<serialization>
<foreign_settings id="place" __inheritance__="Comhon\SqlTable"/>
</serialization>
</root>| 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 requester permit to retrieve one object by its model name and its id. The entrypoint is Comhon\Request\SimpleRequester. It manage all types of serialization. You cannot use it if your model doesn't have id property.
$modelName = 'Test\Person';
$id = 1;
$filterProperties = null;
$isPrivate = true;
$requester = SimpleRequester::build($modelName, $id, $filterProperties, $isPrivate);
$result = $requester->execute(); // return ComhonObject or null if not foundIf 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.
$modelName = 'Test\MyModel';
$id = '[1,"id2",456]';
$filterProperties = null;
$isPrivate = true;
$requester = SimpleRequester::build($modelName, $id, $filterProperties, $isPrivate);
$result = $request->execute(); // return ComhonObject or null if not foundComplex requester permit to retrieve, in same time, several objects that match with some given filters. Only objects with an SQL database serialization (Comhon\SqlTable) might be requested, and filter's models must be linked to the same SQL database.
The entrypoint to build and execute complex requests is Comhon\Request\ComplexRequester.
$requester = ComplexRequester::build($request, $isPrivate);
$result = $requester->execute(); // return ComhonArrayThe first parameter might be a comhon object or an interfaced object (StdClass, array, DomNode, SimpleXMLElement). if an interfaced object is given, it will be imported in comhon object.
Model of comhon object request must be either Comhon\Request\Intermediate or Comhon\Request\Complex.
The intermediate request format correspond to model Comhon\Request\Intermediate.
You can see associated manifest here.
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
{
"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"
}
]
}
}Stringified ComhonArray :
[
{
"id": 2,
"surface": 130,
"garden": true,
"owner": 2
},
{
"id": 3,
"surface": 120,
"garden": true,
"owner": 2
}
]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
filterthat is aliteralor aclause: the filter to apply
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
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
the logical form of previous request would be : (a ∨ b) ∧ c ∧ d ∧ e
I want persons
-named Paul or john
-and which have between 2 and 6 grandchildren
{
"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
}
]
}
}
]
}
}Stringified ComhonArray :
[
{
"id": 1,
"firstName": "john",
"lastName": "doe",
"birthPlace": "1"
}
]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
havingthat is ahaving literalor ahaving clause
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)
A having clause is like a simple clause but can only contain having literals and having clauses.
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
COUNTfunction) - operator (managed operators :
=,<>,<,>,<=,>=) - value
- Imagine we want to add a property
addressto modelTest\Houseand this property would have typeTest\Place(same type as propertybirthPlacein modelTest\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.
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.
I want persons
-that have a grandson named walter
-and that have a house without garden
{
"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
}
]
}
}To have a better compehension here is the tree in more visual way :
person (p1)
___________|__________
| |
children (p2) houses (houses)
|
children (p3)
Each node tree has :
- a property
propertyormodel- root node contain
modelthat is the model name of requested objects. - other nodes contain
propertythat is a property name of the model of the parent node.
- root node contain
- a property
idthat 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.
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.
- 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 (
ASCorDESC). - 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"}]
}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 integerIt 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