-
Notifications
You must be signed in to change notification settings - Fork 0
Object management
- 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 store all deserialized (loaded) and imported objects. This singleton is usefull to know which object already exists and avoid redundants deserializations.
$object = MainObjectCollection::getInstance()->getObject('an_id', 'a_model_name');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 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.
$object = $modelOne->loadObject('12');
// 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('[1,1501774389]');Thanks to MainObjectCollection if an object has already been loaded it will not be loaded again (except if you force it).
// load object
$object = $modelOne->loadObject('12');
// return same instance object retrieve from MainObjectCollection
$object = $modelOne->loadObject('12');
// like previous instruction but force to call deserialization
// and merge current object instance with serialized object
$object = $modelOne->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"}'Define if interfacer have to export only updated values (ids are always exported even if they are not 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'// merge (type merge is used by default if you don't specify it)
// if an object with same model and same id is found in MainObjectCollection get this instance
// otherwise create a new one
// finally merge retrieved (or created) object with values to import
$person = $personModel->fromOject(json_decode('{"id":1,"firstName":"john","lastName":"doe"}'), Model::MERGE);
// overwrite
// if an object with same model and same id is found in MainObjectCollection get this instance and erase all values
// otherwise create a new one
// finally fill retrieved (or created) object with values to import
$person = $personModel->fromOject(json_decode('{"id":1,"firstName":"john","lastName":"doe"}'), Model::OVERWRITE);
// no merge
// create a new instance even if an instance exists in MainObjectCollection
// finally fill created object with values to import
$person = $personModel->fromOject(json_decode('{"id":1,"firstName":"john","lastName":"doe"}'), Model::NO_MERGE);We have seen in Getting started page that a simple model dateTime exists so a property can have a type dateTime. A value with type dateTime will be an instance of DateTime. Using DateTime is a good practice, there are many date and time related functions and it can handle time zones.
When you import or export object you can specify an optional parameter that define time zone that you want to work with (by default your PHP time zone is used).
// see php DateTime Constructor to know supported date time string formats
// use ISO 8601 format is a good practice
$person = $personModel->fromOject(json_decode('{"birthDate":"1988-05-01T14:53:54+02:00"}'), Model::MERGE, 'Europe/Paris');
$date = $person->getValue('birthDate'); // return instance of DateTime
// dateTime values are always exported in ISO 8601 format to encourage good practice
$person->toXml(false, 'Europe/Paris');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.