Skip to content
comhon-project edited this page Aug 23, 2017 · 58 revisions

Table of contents

  1. Preamble
  2. Object Collection
    1. Inheritance
  3. Main Object Collection
  4. Object serialization
    1. Serialization
    2. Deserialization
      1. Via model
      2. Via object value
        1. Unique value
        2. Aggregation value
    3. Database time zone
  5. Object interfacing
    1. Export
      1. Foreign properties export
      2. Inheritance
        1. Object properties
        2. Foreign properties
    2. import
      1. Foreign properties import
    3. Interfacing preferences
      1. privateContext
      2. serialContext
      3. dateTimeFormat
      4. dateTimeZone
      5. updatedValueOnly
      6. propertiesFilters
      7. flattenValues
      8. exportMainForeignObjects
      9. flagValuesAsUpdated
      10. flagObjectAsLoaded
      11. mergeType
  6. Object cast
    1. Example
    2. Limitation
    3. Automatic cast

Preamble

For the most of following examples we will use person model defined in manifest person example. We will use man and woman too that inherit from person. We suppose that these three models have the same serialization.

Object Collection

An Object Collection permit to store objects and find them quickly and easily. Objects are indexed 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 :

$person = new Object('person');
$person->setId(1);

$objectCollection = new OjectCollection();
$objectCollection->addObject($person);

$objectCollection->hasObject(1, 'person');           // return true
$object = $objectCollection->getObject(1, 'person'); // return object previously added

$objectCollection->hasObject(2, 'person');           // return false
$object = $objectCollection->getObject(2, 'person'); // return null

$objectCollection->hasObject(1, 'house');            // return false
$object = $objectCollection->getObject(1, 'house');  // return null

Be careful if you update your object id, it will not be reindexed (you have to do it yourself if necessary).

$person = new Object('person');
$person->setId(1);

$objectCollection = new OjectCollection();
$objectCollection->addObject($person);

$person->setId(2);

$objectCollection->hasObject(1, 'person');           // return true
$object = $objectCollection->getObject(1, 'person'); // return object previously added

$objectCollection->hasObject(2, 'person');           // return false
$object = $objectCollection->getObject(2, 'person'); // return null

Inheritance

To explain inheritance in Object Collection let's start with an example. We remind that, man that inherit from person and these models have the same serialization.
If an Object with model man is added in Object Collection, you can access it by using model name person.

$man = new Object('man');
$man->setId(1);

$objectCollection = new OjectCollection();
$objectCollection->addObject($man);

$objectCollection->hasObject(1, 'person'); // return true
$object = $objectCollection->getObject(1, 'person'); // return object previously added

It works in the other way

$person = new Object('person');
$person->setId(2);

$objectCollection = new OjectCollection();
$objectCollection->addObject($person);

$objectCollection->hasObject(2, 'man'); // return true
$object = $objectCollection->getObject(2, 'man'); // return object previously added

Note that it is possible only if models have the same serialization.

Main Object Collection

MainObjectCollection 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 found

Objects are automatically added when :

$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'
$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'

Contrary to an instance of ObjectCollection that you have instanciated yourself, Comhon! is able to reindex objects in MainObjectCollection if their id have been updated.

$person = new Object('person');
$person->setValue('id', 1);
$person2 = MainObjectCollection::getInstance()->getObject(1, 'person'); // return same instance as $person

$person->setValue('id', 2);
$person2 = MainObjectCollection::getInstance()->getObject(1, 'person'); // return null
$person2 = MainObjectCollection::getInstance()->getObject(2, 'person'); // return same instance as $person

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'

Object serialization

Serialization

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();

Specific case for sql database

Your table has an incremental id

Like other serializations call save() function but there are two cases to identify :

  1. if your Object doesn't have id set, an INSERT will be executed. if INSERT is successfull, id will be set in your object.
  2. if Object has id set, an UPDATE will be executed

Your table doesn't have an incremental id

you must specify your operation (insert or update)

$object->save(SqlTable::INSERT);
$object->save(SqlTable::UPDATE);

Deserialization

Whatever is your serialzation you just have to call a simple function to deserialize your object. Obviously the model associated to your object must have a serialization.

Via model

$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"]');

When you load an object, this object is stored in MainObjectCollection. And 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);

Via object value

When an object is loaded, foreign properties of this object that have their own serialization are instanciate but are not loaded. Like in previous section, if a value has already been loaded it will not be loaded again (except if you force it).

For example you have loaded a person, values like mother or children are instanciated but are not loaded.

$model = ModelManager::getInstance()->getInstanceModel('person');
$person = $model->loadObject(1);
echo $person->isLoaded(); // output true

echo $person->getValue('mother')->isLoaded(); // output false
echo $person->getValue('children')->isLoaded(); // output false

Unique value

To load a unique value call loadValue function :

$person->loadValue('mother');
echo $person->getValue('mother')->isLoaded(); // output true

Aggregation value

  • To fully load an aggregation value call loadValue function :
$person->loadValue('children');
echo $person->getValue('children')->isLoaded(); // output true

foreach ($person->getValue('children') as $child) {
    // each child has all values set, and is loaded
    echo $child->isLoaded(); // output true
}
  • To load only aggregation ids call loadAggregationIds function :
$person->loadAggregationIds('children');
echo $person->getValue('children')->isLoaded(); // output true

foreach ($person->getValue('children') as $child) {
    // each child has id set, but is not loaded
    echo $child->isLoaded(); // output false
}

Database time zone

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.

Object interfacing

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.

export

Let's start with an example

// create object that will be exported and add simple values
$person = new Object('man');
$person->setValue('id', 1); // by setting id, object is added in MainObjectCollection
$person->setValue('firstName', 'John');
$person->setValue('lastName', 'Doe');

// create and add mother value
$woman = new Object('woman');
$woman->setValue('id', 2); // by setting id, object is added in MainObjectCollection
$woman->setValue('firstName', 'Jane');
$woman->setValue('lastName', 'Doe');
$person->setValue('mother', $woman);

// create and add children values
$childOne = new Object('woman');
$childOne->setValue('id', 3); // by setting id, object is added in MainObjectCollection

$childTwo = new Object('man');
$childTwo->setValue('id', 4); // by setting id, object is added in MainObjectCollection

$children = $person->initValue('children');
$children->pushValue($childOne);
$children->pushValue($childTwo);

// create and add bodyArts values
$tattooOne = new Object('tattoo');
$tattooOne->setValue('type', 'dragon');
$tattooOne->setValue('location', 'back');

$tattooTwo = new Object('tattoo');
$tattooTwo->setValue('type', 'flower');
$tattooTwo->setValue('location', 'arm');

$bodyArts = $person->initValue('bodyArts');
$bodyArts->pushValue($tattooOne);
$bodyArts->pushValue($tattooTwo);

Now object is built, we can export it

$XMLInterfacer = new XMLInterfacer();

// via interfacer
$exportedPerson = $XMLInterfacer->export($person); // return instance of DOMNode

// via comhon object
$exportedPerson = $person->export($XMLInterfacer); // return instance of DOMNode

// via model (be careful when you use this method, $model and $person model must match)
$exportedPerson = $model->export($person, $XMLInterfacer); // return instance of DOMNode

// stringify exported object
$xml = $XMLInterfacer->toString($exportedPerson); // return XML string
echo $xml;

output :

<person id="1" firstName="John" lastName="Doe">
    <bodyArts>
        <bodyArt type="dragon" location="back" __inheritance__="tattoo"/>
        <bodyArt type="flower" location="arm" __inheritance__="tattoo"/>
    </bodyArts>
    <mother>2</mother>
    <children>
        <child id="3" __inheritance__="man"/>
        <child id="4" __inheritance__="woman"/>
    </children>
</person>

Foreign properties export

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. If exported object had several id properties, it would be exported has JSON encoded array.
Children are foreign too, but exported node is not a simple id value, we will explain why in following section.

Inheritance

Object properties

Like described in manifest person example bodyArts property has bodyArt type. And In previous example we have added tattoos objects. Actually we can add tattoo because it inherit from bodyArt. So interfaced object tattoo must include it model name otherwise we would not be able to recognize if object is a tattoo, a piercing or simply a bodyArt. The object model name is specified under the key __inheritance__.

Foreign properties

Like described in manifest person example children property has person type. And In previous example we have added woman and man objects. Actually we can add them because they inherit from person. Like "normal" object properties we have to specify model name, so instead of export only an id, we have to export an object that contain __inheritance__ and id. Note that id is always id, even if a model would have an id named "my_id" or several id.

import

$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'

Foreign properties import

When a value corresponding to a foreign property is imported, it has only id information and optionally __inheritance__ information (seen in previous section). Import will automatically try to find if referenced object exists in current object or in Main Object Collection and get this instance, otherwise it will create a new object.
To explain described behavior we add an id property id to bodyArt and we add a foreign property foreignTattoo with type tattoo to person. It doesn't make sens in model but do not take this into account, it's just an example

$model = ModelManager::getInstance()->getInstanceModel('person');
$stdOject = json_decode('{
    "id": 10,
    "firstName": "John",
    "lastName": "Doe",
    "mother": 20,
    "children": [
        {"__inheritance__": "man","id": 30},
        {"__inheritance__": "woman","id": 40}
    ],
    "bodyArts": [
        {"__inheritance__":"tattoo","id":1,"type":"dragon","location":"back"},
        {"__inheritance__":"tattoo","id":2,"type":"flower","location":"arm"}
    ],
    "foreignTattoo":1
}');

$woman = new Object($model);
$woman->setValue('id', 20); // by setting id, object is added in MainObjectCollection
$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 40

echo $importedPerson->getValue('mother')->getValue('firstName');
// output 'Jane'
echo $woman === $importedPerson->getValue('mother') ? 'same instance' : 'different instance';
// output 'same instance'

echo $importedPerson->getValue('foreignTattoo')->getValue('type');
// output 'dragon'
echo $importedPerson->getValue('foreignTattoo') === $importedPerson->getValue('bodyArts')->getValue(0)
        ? 'same instance' : 'different instance';
// output 'same instance'

Interfacing preferences

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 context

privateContext

Summary

Define private context (context might be private or public).
Private properties are interfaced only in private context.

Description

type boolean
default false
availability import and export
constant Interfacer::PRIVATE_CONTEXT

Example

// 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'

serialContext

Summary

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.

Description

type boolean
default false
availability import and export
constant Interfacer::SERIAL_CONTEXT

Example

// 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"}'

dateTimeFormat

Summary

Define date time format.
dateTime values will be exported in specified date time format.

Description

type string
default ISO 8601
availability export
constant Interfacer::DATE_TIME_FORMAT

Example

$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"}'

dateTimeZone

Summary

Define date time zone.
dateTime values will be interfaced in specified date time zone.

Description

type string
default your PHP time zone
availability import and export
constant Interfacer::DATE_TIME_ZONE

Example

$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')

updatedValueOnly

Summary

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.

Description

type boolean
default false
availability export
constant Interfacer::ONLY_UPDATED_VALUES

Example

$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 updated

propertiesFilters

Summary

Define a filter of values that will be exported.

Description

type array
default null
availability export
constant Interfacer::PROPERTIES_FILTERS

Example

$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');

$bodyArts = $person->initValue('bodyArts');
$bodyArts->pushValue($tattooOne);
$bodyArts->pushValue($tattooTwo);

$stdInterfacer = new StdObjectInterfacer();
$filter = [
    'person' => ['id', 'bodyArts'],
    'tattoo' => ['type'],
];

$stdObject = $stdInterfacer->export($person, [Interfacer::PROPERTIES_FILTERS => $filter]);
echo $stdInterfacer->toString($stdObject);
// output '{"id":1,"bodyArts":[{"type":"dragon","__inheritance__": "tattoo"},{"type":"flower","__inheritance__":"tattoo"}]}'

flattenValues

Summary

Define if interfaced complex values (object or array) are flattened (stringified).
This preference is used typically during sql serialization.

Description

type boolean
default false
availability export and import
constant Interfacer::FLATTEN_VALUES

Example

$person = new Object('person');
$person->setValue('id', 1);
$person->setValue('firstName', 'John');

$tattooOne = new Object('tattoo');
$tattooOne->setValue('type', 'dragon');
$tattooOne->setValue('location', 'arm');

$bodyArts = $person->initValue('bodyArts');
$bodyArts->pushValue($tattooOne);

$stdInterfacer = new StdObjectInterfacer();

$stdObject = $stdInterfacer->export($person, [Interfacer::FLATTEN_VALUES => true]);
echo $stdInterfacer->toString($stdObject);
// output '{"id":1,"firstName":"John","bodyArts":"[{\"type\":\"dragon\",\"location\":\"arm\",\"__inheritance__\":\"tattoo\"}]"}'

exportMainForeignObjects

Summary

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();

Description

type boolean
default false
availability export
constant Interfacer::EXPORT_MAIN_FOREIGN_OBJECTS

Example

$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"}}}'

flagValuesAsUpdated

Summary

Define if imported values will be flagged as updated.

Description

type boolean
default true
availability import
constant Interfacer::FLAG_VALUES_AS_UPDATED

Example

$stdInterfacer = new StdObjectInterfacer();

$stdObject = json_decode('{"id":1,"firstName":"John","bodyArts":[{"type":"dragon","location":"arm","__inheritance__":"tattoo"}]}');
$person = $stdInterfacer->import($stdObject);
echo $person->isUpdatedValue('firstName'); // output true
echo $person->getValue('bodyArts')->getValue(0)->isUpdatedValue('type'); // output true

$stdObject = json_decode('{"id":2,"firstName":"Jane","bodyArts":[{"type":"dragon","location":"arm","__inheritance__":"tattoo"}]}');
$person = $stdInterfacer->import($stdObject, [Interfacer::FLAG_VALUES_AS_UPDATED => false]);
echo $person->isUpdatedValue('firstName'); // output false
echo $person->getValue('bodyArts')->getValue(0)->isUpdatedValue('type'); // output false

flagObjectAsLoaded

Summary

Define 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.

Description

type boolean
default true
availability import
constant Interfacer::FLAG_OBJECT_AS_LOADED

Example

$stdInterfacer = new StdObjectInterfacer();

$stdObject = json_decode('{"id":1,"firstName":"John","bodyArts":[{"type":"dragon","location":"arm","__inheritance__":"tattoo"}]}');
$person = $stdInterfacer->import($stdObject);
echo $person->isLoaded(); // output true
echo $person->getValue('bodyArts')->getValue(0)->isLoaded(); // output true

$stdObject = json_decode('{"id":2,"firstName":"Jane","bodyArts":[{"type":"dragon","location":"arm","__inheritance__":"tattoo"}]}');
$person = $stdInterfacer->import($stdObject, [Interfacer::FLAG_OBJECT_AS_LOADED => false]);
echo $person->isLoaded(); // output false
echo $person->getValue('bodyArts')->getValue(0)->isLoaded(); // output true

mergeType

Summary

Define 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 in MainObjectCollection.
    • 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 in MainObjectCollection.
    • 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 in MainObjectCollection. 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.

Description

type enumeration (1,2,3) (Interfacer::MERGE,Interfacer::OVERWRITE,Interfacer::NO_MERGE)
default Interfacer::MERGE
availability import
constant Interfacer::MERGE_TYPE

Example

$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'

Object cast

Comhon! allow a kind of cast by affecting a new model to object. The new model must inherit from previous object model. For example you have instanciate an object with model person you might cast this object with model woman. It's often used automatically with foreign properties and sql serialization.

Example

For this example we will assume that model woman has an additional property than person. This property is maidenName.

$person = new Object('person');
$person->getModel()->getName(); // output 'person'
$person->setValue('maidenName', 'Doe'); // throw exception because 'maidenName' doesn't exist in model 'person'

$modelWoman = ModelManager::getInstance()->getInstanceModel('woman');

$person->cast($modelWoman);
echo $person->getModel()->getName(); // output 'woman'
$person->setValue('maidenName', 'Doe');
echo $person->getValue('maidenName'); // output 'Doe'

Limitation

If you have defined your own class Person and Woman. Model will be updated like we said before but Object will keep same class and it can be confusing.

$person = new Person();
$person->getModel()->getName(); // output 'person'
echo get_class($person); // output 'Your\Namespace\Person'

$modelWoman = ModelManager::getInstance()->getInstanceModel('woman');

$person->cast($modelWoman);
echo $person->getModel()->getName(); // output 'woman'
echo get_class($person); // output 'Your\Namespace\Person'

Automatic cast

As we have mentioned before, some cast might be made automatically. Let's take an example :
Like described in manifest person example, model person has a property bestFriend with type person (actually it might be a man or a woman). Objects with models person, man and woman are serialized in same sql table person. This table have a column best_friend_id (with integer type) that is a foreign key of person id.
If we load a person we are not able to know if his best friend is a woman or a man except with an other database request. So best friend will be instanciated with model person.
If later we load best friend object, it will be automatically casted to woman or a man.

Here a simplified example of serialized persons :

id sex first_name last_name best_friend_id
1 male john doe 2
2 male john smith 1

First we load person with id 1 and then person with id 2

$modelPerson = ModelManager::getInstance()->getInstanceModel('person');
$modelPerson->loadObject(1);
echo $person->getValue('lastName'); // output 'doe'

echo $person->getValue('bestFriend')->isLoaded(); // output false
echo $person->getValue('bestFriend')->getModel()->getName(); // output 'person'
echo $person->getValue('bestFriend')->getValue('id'); // output 2
echo $person->getValue('bestFriend')->getValue('lastName'); // output NULL

// two equivalent ways to load 'bestFriend' value (first one is recommended) 
$person->loadValue('bestFriend');
$modelPerson->loadObject($person->getValue('bestFriend')->getId());

echo $person->getValue('bestFriend')->isLoaded(); // output true
echo $person->getValue('bestFriend')->getModel()->getName(); // output 'man'
echo $person->getValue('bestFriend')->getValue('lastName'); // output 'smith'

// bonus
echo $person === $person->getValue('bestFriend')->getValue('bestFriend') ? 'same instance' : 'different instance';
// output 'same instance'

In general, automatic casts are made during deserialization and import.

Clone this wiki locally