-
Notifications
You must be signed in to change notification settings - Fork 0
Object management
- Object Collection
- Main Object Collection
- Object serialization
- Object deserialization
- Object interfacing
- Date time management
An Object Collection permit to store objects and find them in constant time. Objects are grouped by model and by id. Obviously an object without id property cannot be stored in Object Collection.
Example of an Object Collection structure :
[
person : [
an_id_one : person_object_one,
an_id_two : person_object_two
],
town : [
an_id_one : town_object_one
],
house : [
an_id_one : house_object_one,
an_id_two : house_object_two
]
]
Example of usage :
$lPersonOne; // consider object already exists, has a model Person and has an id 'one'
$objectCollection = new OjectCollection();
$objectCollection->addObject($lPersonOne);
$objectCollection->hasObject('one', 'Person'); // return true
$object = $objectCollection->getObject('one', 'Person'); // return object previously added
$objectCollection->hasObject('two', 'Person'); // return false
$object = $objectCollection->getObject('two', 'Person'); // return null
$objectCollection->hasObject('one', 'House'); // return false
$object = $objectCollection->getObject('one', 'House'); // return nullMainObjectCollection is a singleton that extends ObjectCollection. It store automatically objects with MainModel having id property(ies). This singleton is useful to know which object already exists and avoid redundant deserializations.
To retrieve an object call :
$object = MainObjectCollection::getInstance()->getObject('an_id', 'a_model_name'); // null if object not foundObjects are automatically added when :
- object is deserialized (see deserialization section)
$model = ModelManager::getInstance()->getInstanceModel('person');
$person1 = MainObjectCollection::getInstance()->getObject(1, 'person'); // null
$person2 = $model->loadObject(1);
$person3 = MainObjectCollection::getInstance()->getObject(1, 'person');
echo $person2 === $person3 ? 'same instance' : 'different instance';
// output 'same instance'- when import interfaced object (see Object interfacing section)
$model = ModelManager::getInstance()->getInstanceModel('person');
$stdInterfacer = new StdObjectInterfacer();
$stdOject = json_decode('{"id":1,"firstName":"John"}');
$person1 = MainObjectCollection::getInstance()->getObject(1, 'person'); // null
$person2 = $stdInterfacer->import($stdOject, $model);
$person3 = MainObjectCollection::getInstance()->getObject(1, 'person');
echo $person2 === $person3 ? 'same instance' : 'different instance';
// output 'same instance'- when you set id of an object
$person1 = MainObjectCollection::getInstance()->getObject(1, 'person'); // null
$person2 = new Object('person');
$person2->setValue('id', 1);
$person3 = MainObjectCollection::getInstance()->getObject(1, 'person');
echo $person2 === $person3 ? 'same instance' : 'different instance';
// output 'same instance'Note that if an instance is already added in MainObjectCollection it will not be replaced.
$person = new Object('person');
$person->setValue('id', 1);
$person2 = new Object('person');
$person2->setValue('id', 1);
$person3 = MainObjectCollection::getInstance()->getObject(1, 'person');
echo $person === $person3 ? 'same instance' : 'different instance';
// output 'same instance'
echo $person2 === $person3 ? 'same instance' : 'different instance';
// output 'different instance'Whatever is your serialzation you just have to call one function save() to serialize your object. Obviously the model associated to your object must have a defined serialization.
$object->save();Like other serializations call save() function but there are two cases to identify :
- if your Object doesn't have id set, an
INSERTwill be executed. ifINSERTis successfull, id will be set in your object. - if Object has id set, an
UPDATEwill be executed
you must specify your operation (insert or update)
$object->save(SqlTable::INSERT);
$object->save(SqlTable::UPDATE);Whatever is your serialzation you just have to call one function loadObject() to deserialize your object. Obviously the model associated to your object must have a serialization. When you load an object this object is stored in MainObjectCollection.
$model = ModelManager::getInstance()->getInstanceModel('a_model_name');
$object = $model->loadObject('an_id');
// if your model has several id properties you must specify them in a json encoded array
// order of values must be the same as order of id properties in manifest
$object = $modelTwo->loadObject('["an_id_1","an_id_2"]');Thanks to MainObjectCollection if an object has already been loaded it will not be loaded again (except if you force it).
// load object
$object = $model->loadObject('12');
// return same instance object retrieve from MainObjectCollection
$object = $model->loadObject('12');
// like previous instruction but force to call deserialization
// and merge current object instance with serialized object
$object = $model->loadObject('12', true);Interfacing consist to import entry in specific format to populate a comhon object or conversely export comhon object to specific format. Comhon! provide three interfacer :
| interfacer | handled format | handled parsed format |
|---|---|---|
| StdObjectInterfacer | JSON | stdClass |
| AssocArrayInterfacer | JSON | array |
| XMLInterfacer | XML | DOMElement |
All interfacers extends Interfacer so they are very similar and have the same management. We will give example with a random interfacer but keep in mind that every examples are possible with all interfacers.
$model = ModelManager::getInstance()->getInstanceModel('person');
$stdOject = json_decode('{"id":1,"firstName":"John"}');
$stdInterfacer = new StdObjectInterfacer();
// via interfacer
$importedPerson = $stdInterfacer->import($stdOject, $model); // return instance of ComhonObject
echo $importedPerson->getValue('firstName'); // output 'John'
// via model
$importedPerson = $model->import($stdOject, $stdInterfacer); // return instance of ComhonObject
echo $importedPerson->getValue('firstName'); // output 'John'
// via comhon object
$person = new Object($model);
$person->setValue('lastName', 'Doe');
$person->fill($stdOject, $stdInterfacer); // fill current instance of ComhonObject
echo $person->getValue('firstName'); // output 'John'
echo $person->getValue('lastName'); // output 'Doe'When a value corresponding to a foreign property is imported it's only an id. Import will automaticaly try to find if referenced object exists in Main Object Collection and get this instance otherwise it will create a new object.
$model = ModelManager::getInstance()->getInstanceModel('person');
$stdOject = json_decode('{"id":1,"firstName":"John","lastName":"Doe","mother":2,"children":[3,4]}');
$woman = new Object($model);
$woman->setValue('id', 2);
$woman->setValue('firstName', 'Jane');
$woman->setValue('lastName', 'Doe');
$stdInterfacer = new StdObjectInterfacer();
$importedPerson = $stdInterfacer->import($stdOject, $model); // return instance of ComhonObject
echo $importedPerson->getValue('children')->getValue(1)->getValue('id');
// output 4
echo $importedPerson->getValue('mother')->getValue('firstName');
// output 'Jane'
echo $woman === $importedPerson->getValue('mother') ? 'same instance' : 'different instance';
// output 'same instance'$model; // we take model previously instanciated in import
$person; // we take person previously set in import
$XMLInterfacer = new XMLInterfacer();
// via interfacer
$exportedStdObject = $XMLInterfacer->export($person); // return instance of DOMNode
// via comhon object
$exportedStdObject = $person->export($XMLInterfacer); // return instance of DOMNode
// via model (be careful when you use this method, $model and $person model must match)
$exportedStdObject = $model->export($person, $XMLInterfacer); // return instance of DOMNode
// stringify exported object
$xml = $XMLInterfacer->toString($exportedStdObject); // return XML string
echo $xml;output :
<person id="1" firstName="John" lastName="Doe">
<mother>2</mother>
<children>
<child>3</child>
<child>4</child>
</children>
</person>As you can see in previous example, value mother is exported as a simple id even if mother is actually an object. Foreign properties are exported like that for a simple reason, value can be defined elsewhere, and if we describe all object, same value would be described several times, it can be ambiguous and dangerous.
You can define some preferences to apply during interfacing. All preferences have their constantes defined in Interfacer class to permit you to find them easily.
there is two ways to define interfacer preferences :
- by calling setter :
$stdInterfacer = new StdObjectInterfacer();
$stdInterfacer->setPrivateContext(true);
$stdInterfacer->setDateTimeZone('UTC');- during interfacing :
$stdInterfacer = new StdObjectInterfacer();
$preferences = [
Interfacer::PRIVATE_CONTEXT => true,
Interfacer::DATE_TIME_ZONE => 'UTC',
];
$stdObject = $stdInterfacer->export($person, $preferences);If you don't specify preference, the default preference will be used.
If you use same instance of interfacer for several import/export, preferences previously set will be used (or default value)
$stdInterfacer = new StdObjectInterfacer();
// default value for 'privateContext' is boolean false
$stdObject = $stdInterfacer->export($person); // export is NOT in private context
$stdObject = $stdInterfacer->export($person, ['privateContext' => true]); // export is in private context
$stdObject = $stdInterfacer->export($person); // export still in private contextDefine private context (context might be private or public).
Private properties are interfaced only in private context.
| type | boolean |
| default | false |
| availability | import and export |
| constant | Interfacer::PRIVATE_CONTEXT |
// here we suppose 'id' is a private property
$person = new Object('person');
$person->setValue('id', 1);
$person->setValue('firstName', 'John');
$stdInterfacer = new StdObjectInterfacer();
$stdObject = $stdInterfacer->export($person);
echo $stdInterfacer->toString($stdObject);
// output '{"firstName":"John"}'
$stdObject = $stdInterfacer->export($person, [Interfacer::PRIVATE_CONTEXT => true]);
echo $stdInterfacer->toString($stdObject);
// output '{"id":1,"firstName":"John"}'
$stdObject = json_decode({"id":2,"firstName":"Jane"});
$person = $stdInterfacer->import($stdObject, [Interfacer::PRIVATE_CONTEXT => false]);
echo $person->hasValue('id'); // output false
echo $person->getValue('Jane'); // output 'Jane'
$person = $stdInterfacer->import($stdObject, [Interfacer::PRIVATE_CONTEXT => true]);
echo $person->getValue('id'); // output 2
echo $person->getValue('Jane'); // output 'Jane'Define serial context. This preference is used implicitly for serialization/deserialization.
Properties are interfaced with their serialization name.
Aggregation properties are not interfaced in serial context.
| type | boolean |
| default | false |
| availability | import and export |
| constant | Interfacer::SERIAL_CONTEXT |
// here we suppose 'children' is an aggregation property
// and property 'firstName' has serialization name 'first_name'
$person = new Object('person');
$person->setValue('firstName', 'John');
$childOne = new Object('person');
$childOne->setValue('id', 2);
$childTwo = new Object('person');
$childTwo->setValue('id', 3);
$children = $person->initValue('children');
$children->pushValue($childOne);
$children->pushValue($childTwo);
$stdInterfacer = new StdObjectInterfacer();
$stdObject = $stdInterfacer->export($person);
echo $stdInterfacer->toString($stdObject);
// output '{"firstName":"John","children":[2,3]}'
$stdObject = $stdInterfacer->export($person, [Interfacer::SERIAL_CONTEXT => true]);
echo $stdInterfacer->toString($stdObject);
// output '{"first_name":"John"}'Define date time format.
dateTime values will be exported in specified date time format.
| type | string |
| default | ISO 8601 |
| availability | export |
| constant | Interfacer::DATE_TIME_FORMAT |
$person = new Object('person');
$person->setValue('firstName', 'John');
$birthDate = new ComhonDateTime('1988-09-16 16:30', new DateTimeZone('Europe/Paris'));
$person->setValue('birthDate', $birthDate);
$stdInterfacer = new StdObjectInterfacer();
$stdObject = $stdInterfacer->export($person);
echo $stdInterfacer->toString($stdObject);
// output '{"firstName":"John","birthDate":"1988-09-16T16:30:00+02:00"}'
$stdObject = $stdInterfacer->export($person, [Interfacer::DATE_TIME_FORMAT => 'Y-m-d H:i']);
echo $stdInterfacer->toString($stdObject);
// output '{"firstName":"John","birthDate":"1988-09-16 16:30"}'Define date time zone.
dateTime values will be interfaced in specified date time zone.
| type | string |
| default | your PHP time zone |
| availability | import and export |
| constant | Interfacer::DATE_TIME_ZONE |
$person = new Object('person');
$person->setValue('firstName', 'John');
$birthDate = new ComhonDateTime('1988-09-16 16:30', new DateTimeZone('Europe/Paris'));
$person->setValue('birthDate', $birthDate);
$stdInterfacer = new StdObjectInterfacer();
$stdObject = $stdInterfacer->export($person, [Interfacer::DATE_TIME_ZONE => 'Europe/Paris']);
echo $stdInterfacer->toString($stdObject);
// output '{"firstName":"John","birthDate":"1988-09-16T16:30:00+02:00"}'
$stdObject = $stdInterfacer->export($person, [Interfacer::DATE_TIME_ZONE => 'UTC']);
echo $stdInterfacer->toString($stdObject);
// output '{"firstName":"John","birthDate":"1988-09-16T14:30:00+00:00"}'
// import dateTime that doesn't specify it time zone (Y-m-d H:i:s)
$stdObject = json_decode('{"firstName":"John","birthDate":"1988-09-16 16:30:00"}');
$person = $stdInterfacer->import($stdObject, [Interfacer::DATE_TIME_ZONE => 'Europe/Paris']);
echo $person->getValue($stdObject)->format('c');
// output '1988-09-16T16:30:00+02:00' (+02:00 => 'Europe/Paris')
$person = $stdInterfacer->import($stdObject, [Interfacer::DATE_TIME_ZONE => 'UTC']);
echo $person->getValue($stdObject)->format('c');
// output '1988-09-16T16:30:00+00:00' (+00:00 => 'UTC')Define if interfacer have to export only updated values (ids are always exported even if they are not updated).
This preference is applied only on current exported object. If there are "sub"-objects in current exported object, all values (of these sub-objects) will be exported even if they are not flagged as updated.
This preference is used typically for sql serialization.
| type | boolean |
| default | false |
| availability | export |
| constant | Interfacer::ONLY_UPDATED_VALUES |
$person = new Object('person');
// third parameter of setValue() force status of value
// if we specify false, value we be considered as not updated
$person->setValue('id', 1, false);
$person->setValue('firstName', 'John');
$person->setValue('lastName', 'Doe', false);
$stdInterfacer = new StdObjectInterfacer();
$stdObject = $stdInterfacer->export($person, [Interfacer::ONLY_UPDATED_VALUES => true]);
echo $stdInterfacer->toString($stdObject);
// output '{"id":1,"firstName":"John"}'
// lastName is not exported, and id is exported even if it's not updatedDefine a filter of values that will be exported.
| type | array |
| default | null |
| availability | export |
| constant | Interfacer::PROPERTIES_FILTERS |
$person = new Object('person');
$person->setValue('id', 1);
$person->setValue('firstName', 'John');
$person->setValue('lastName', 'Doe');
$tattooOne = new Object('tattoo');
$tattooOne->setValue('type', 'dragon');
$tattooOne->setValue('location', 'back');
$tattooTwo = new Object('tattoo');
$tattooTwo->setValue('type', 'flower');
$tattooTwo->setValue('location', 'arm');
$tattoos = $person->initValue('tattoos');
$tattoos->pushValue($tattooOne);
$tattoos->pushValue($tattooTwo);
$stdInterfacer = new StdObjectInterfacer();
$filter = [
'person' => ['id', 'tattoos'],
'tattoo' => ['type'],
];
$stdObject = $stdInterfacer->export($person, [Interfacer::PROPERTIES_FILTERS => $filter]);
echo $stdInterfacer->toString($stdObject);
// output '{"id":1,"tattoos":[{"type":"dragon"},{"type":"flower"}]}'Define if interfaced complex values (object or array) are flattened (stringified).
This preference is used typically during sql serialization.
| type | boolean |
| default | false |
| availability | export and import |
| constant | Interfacer::FLATTEN_VALUES |
$person = new Object('person');
$person->setValue('id', 1);
$person->setValue('firstName', 'John');
$tattooOne = new Object('tattoo');
$tattooOne->setValue('type', 'dragon');
$tattooOne->setValue('location', 'arm');
$tattoos = $person->initValue('tattoos');
$tattoos->pushValue($tattooOne);
$stdInterfacer = new StdObjectInterfacer();
$stdObject = $stdInterfacer->export($person, [Interfacer::FLATTEN_VALUES => true]);
echo $stdInterfacer->toString($stdObject);
// output '{"id":1,"firstName":"John","tattoos":"[{\"type\":\"dragon\",\"location\":\"arm\"}]"}'Define if foreign values with a main model have to be exported aside to current exported object.
exported foreign values are stored in interfacer and can be retrieved with getter getMainForeignObjects().
exportedObjects = $interfacer->getMainForeignObjects();| type | boolean |
| default | false |
| availability | export |
| constant | Interfacer::EXPORT_MAIN_FOREIGN_OBJECTS |
$person = new Object('person');
$person->setValue('id', 1);
$person->setValue('firstName', 'John');
$mother = new Object('person');
$mother->setValue('id', 2);
$mother->setValue('firstName', 'Jane');
$person->setValue('mother', $mother);
$stdInterfacer = new StdObjectInterfacer();
$stdObject = $stdInterfacer->export($person, [Interfacer::EXPORT_MAIN_FOREIGN_OBJECTS => true]);
echo $stdInterfacer->toString($stdObject);
// output '{"id":1,"firstName":"John","mother":2}'
echo json_encode($stdInterfacer->getMainForeignObjects());
// output '{"person":{"2":{"id":2,"firstName":"Jane"}}}'Define if imported values will be flagged as updated.
| type | boolean |
| default | true |
| availability | import |
| constant | Interfacer::FLAG_VALUES_AS_UPDATED |
$stdInterfacer = new StdObjectInterfacer();
$stdObject = json_decode('{"id":1,"firstName":"John","tattoos":[{"type":"dragon","location":"arm"}]}');
$person = $stdInterfacer->import($stdObject);
echo $person->isUpdatedValue('firstName'); // output true
echo $person->getValue('tattoos')->getValue(0)->isUpdatedValue('type'); // output true
$stdObject = json_decode('{"id":2,"firstName":"Jane","tattoos":[{"type":"dragon","location":"arm"}]}');
$person = $stdInterfacer->import($stdObject, [Interfacer::FLAG_VALUES_AS_UPDATED => false]);
echo $person->isUpdatedValue('firstName'); // output false
echo $person->getValue('tattoos')->getValue(0)->isUpdatedValue('type'); // output falseDefine if imported object will be flagged as loaded.
In fact the real name of this preference in source code is flagObjectAsUpdated but this is a mistake, it will be updated in new version. If you use constant you don't have to take care of it.
| type | boolean |
| default | true |
| availability | import |
| constant | Interfacer::FLAG_OBJECT_AS_LOADED |
$stdInterfacer = new StdObjectInterfacer();
$stdObject = json_decode('{"id":1,"firstName":"John","tattoos":[{"type":"dragon","location":"arm"}]}');
$person = $stdInterfacer->import($stdObject);
echo $person->isLoaded(); // output true
echo $person->getValue('tattoos')->getValue(0)->isLoaded(); // output true
$stdObject = json_decode('{"id":2,"firstName":"Jane","tattoos":[{"type":"dragon","location":"arm"}]}');
$person = $stdInterfacer->import($stdObject, [Interfacer::FLAG_OBJECT_AS_LOADED => false]);
echo $person->isLoaded(); // output false
echo $person->getValue('tattoos')->getValue(0)->isLoaded(); // output trueDefine the merge type used during import. This preference is available only via MainModel::import() or Interfacer::import(), not via comhon object ComhonObject::fill().
There are three merge type :
-
merge :
If imported object has id, try to find instance of comhon object with same id inMainObjectCollection.- If found, get existing comhon object and merge imported values with existing values.
- otherwise create new instance and populate it.
-
overwrite :
If imported object has id, try to find instance of comhon object with same id inMainObjectCollection.- If found, get existing comhon object, delete existing values and populate it.
- otherwise create new instance and populate it.
-
no merge :
Create new instance and populate it even if instance with same id already exists inMainObjectCollection. Use carefully this preference, it's often a bad idea to have different instances of objects with same id. But in some very specific case it might be helpfull.
| type | enumeration (1,2,3) (Interfacer::MERGE,Interfacer::OVERWRITE,Interfacer::NO_MERGE) |
| default | Interfacer::MERGE |
| availability | import |
| constant | Interfacer::MERGE_TYPE |
$person = new Object('person');
$person->setValue('id', 1); // object is added to MainObjectCollection
$person->setValue('firstName', 'John');
$stdObject = json_decode('{"id":1,"lastName":"Doe"}');
$stdInterfacer = new StdObjectInterfacer();
$importedPerson = $stdInterfacer->import($stdObject, [Interfacer::MERGE_TYPE => Interfacer::MERGE]);
echo $importedPerson->getValue('firstName'); // output 'John'
echo $importedPerson->getValue('lastName'); // output 'Doe'
echo $person === $importedPerson ? 'same instance' : 'different instance';
// output 'same instance'
$importedPerson = $stdInterfacer->import($stdObject, [Interfacer::MERGE_TYPE => Interfacer::OVERWRITE]);
echo $importedPerson->hasValue('firstName'); // output false
echo $importedPerson->getValue('lastName'); // output 'Doe'
echo $person === $importedPerson ? 'same instance' : 'different instance';
// output 'same instance'
$importedPerson = $stdInterfacer->import($stdObject, [Interfacer::MERGE_TYPE => Interfacer::NO_MERGE]);
echo $importedPerson->hasValue('firstName'); // output false
echo $importedPerson->getValue('lastName'); // output 'Doe'
echo $person === $importedPerson ? 'same instance' : 'different instance';
// output 'different instance'TODO explain inherithance in object collection, woman registered, person can retrieve object (with same instance).
For most (all?) SGBD connection you can define your time zone. to set a specific time zone for your connection you must specify it in file config.json (by default time zone connection is UTC).
Whatever is your database time zone you don't have to take care of it, serialization/deserialization will automatically affect good time zone to all your values with type dateTime.