{"id":17122316,"url":"https://github.com/sixarmdonkey/magicgraph","last_synced_at":"2025-03-24T02:47:16.385Z","repository":{"id":56973162,"uuid":"251726426","full_name":"SixArmDonkey/magicgraph","owner":"SixArmDonkey","description":"Behavioral-based object modeling and persistence library for PHP 8","archived":false,"fork":false,"pushed_at":"2023-02-18T18:25:38.000Z","size":1245,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-29T09:13:25.121Z","etag":null,"topics":["behavioral-strategies","data-mapper","orm","php8"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"osl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SixArmDonkey.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-31T20:41:39.000Z","updated_at":"2022-04-27T01:31:33.000Z","dependencies_parsed_at":"2024-10-14T18:07:30.011Z","dependency_job_id":"8d3fcf99-091e-402b-b99f-3ad680a8789c","html_url":"https://github.com/SixArmDonkey/magicgraph","commit_stats":{"total_commits":117,"total_committers":2,"mean_commits":58.5,"dds":"0.017094017094017144","last_synced_commit":"4207e772528697cfefcfa0247d6dda5d7d609b9a"},"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SixArmDonkey%2Fmagicgraph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SixArmDonkey%2Fmagicgraph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SixArmDonkey%2Fmagicgraph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SixArmDonkey%2Fmagicgraph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SixArmDonkey","download_url":"https://codeload.github.com/SixArmDonkey/magicgraph/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245200677,"owners_count":20576673,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["behavioral-strategies","data-mapper","orm","php8"],"created_at":"2024-10-14T18:07:23.743Z","updated_at":"2025-03-24T02:47:16.362Z","avatar_url":"https://github.com/SixArmDonkey.png","language":"PHP","readme":"# BuffaloKiwi Magic Graph\n  \n**Behavioral-based object modeling, mapping and persistence library for PHP 8**  \n  \nOSL 3.0 License\n  \n---\n\n## Table of Contents\n\n[Generated Documentation](https://sixarmdonkey.github.io/magicgraph/)\n\nDocumentation is a work in progress.\n\n1. [Overview](#overview)\n2. [Installation](#installation)\n3. [Dependencies](#dependencies)\n4. [Definitions](#definitions)\n5. [Getting Started](#getting-started)\n    1. [Hello Model](#hello-model)\n    2. [Basic Database and Repository Setup](#basic-database-and-repository-setup)\n6. [Property Configuration](#property-configuration)\n    1. [Property Configuration Array Attributes](#property-configuration-array-attributes)\n    2. [Property Data Types](#property-data-types)\n    3. [Property Flags](#property-flags)\n    4. [Property Behavior](#property-behavior)\n    5. [Quick Models](#quick-models)\n    6. [Annotations](#annotations)\n7. [Repositories](#repositories)\n    1. [Mapping Object Factory](#mapping-object-factory)\n    2. [Saveable Mapping Object Factory](#saveable-mapping-object-factory)\n    3. [SQL Repository](#sql-repository)\n    4. [Decorating Repositories](#decorating-repositories)\n    5. [Serviceable Repository](#serviceable-repository)\n    6. [Composite Primary Keys](#composite-primary-keys)\n8. [Transactions](#transactions)\n    1. [Overview](#transactions-overview)\n    2. [Creating a Transaction](#creating-a-transaction)\n    3. [Transaction Factory](#transaction-factory)\n9. [Model service providers](#model-relationship-providers)\n    1. [Serviceable Model](#serviceable-model)\n    2. [Serviceable Repository](#serviceable-repository)\n10. [Relationships](#relationships)\n    1. [One to One](#one-to-one)\n    2. [One to Many](#one-to-many)\n    3. [Many to Many](#many-to-many)\n    4. [Nested Relationship Providers](#nested-relationship-providers)\n    5. [How Editing and Saving Works](#how-editing-and-saving-works)\n11. [Extensible Models](#extensible-models)\n    1. [Property configuration interface](#property-configuration-interface)\n    2. [Property configuration implementation](#property-configuration-implementation)\n    3. [Using multiple property configurations](#using-multiple-property-configurations)\n    4. [Model interface](#model-interface)\n    5. [Model implementation](#model-implementation)\n12. [Behavioral Strategies ](#behavioral-strategies)\n13. [Database Connections](#database-connections)\n    1. [PDO](#pdo)\n    2. [MySQL PDO](#mysql-pdo)\n    3. [Connection Factories](#connection-factories)\n14. [Working with Currency](#working-with-currency)\n15. [Creating HTML elements](#creating-html-elements)\n16. [Magic Graph Setup](#magic-graph-setup)\n17. [Entity-Attribute-Value (EAV)](#entity-attribute-value)\n18. [Searching](#searching)\n19. Extending Magic Graph \n    1. [The Config Mapper](#the-config-mapper)\n20. Tutorial\n\n---\n  \n\n## Overview\n\nMagic graph is an object mapping and persistence library written in pure PHP.  Magic Graph makes it easy to design \nand use rich hierarchical domain models, which may incorporate various independently designed and tested behavioral \nstrategies.  \n\n### Goals\n\nThis is the original set of goals for this project:\n\n1.  Easily create self-validating models\n2.  Dynamically create models at runtime\n3.  Separate model behavior from the model object\n4.  Allow models to be extended by third parties without modifying the model object or subclassing\n  \n  \n**Persistence**\n\nMagic Graph persistence uses the repository and unit of work patterns.  Currently Magic Graph includes MySQL/MariaDB adapters \nout of the box, and additional adapters will be added in future releases.\n\n---\n\n## Installation\n\n```\ncomposer require buffalokiwi/magicgraph\n```\n  \n\n---\n  \n  \n## Dependencies\n\nMagic Graph requires one third party and three BuffaloKiwi libraries.\n\n1. [BuffaloKiwi/buffalotools_ioc](https://github.com/SixArmDonkey/buffalotools_ioc) - A service locator \n2. [BuffaloKiwi/buffalotools_types](https://github.com/SixArmDonkey/buffalotools_types) - Enum and Set support\n3. [BuffaloKiwi/buffalotools_date](https://github.com/SixArmDonkey/buffalotools_date) - DateTime factory/wrappers \n4. [MoneyPHP/Money](https://github.com/moneyphp/money) - PHP implementation of Fowler's Money pattern\n  \n\n---\n  \n  \n## Definitions\n\n### What is a Model?\n\nMagic Graph models are extensible and self-contained programs.  They are designed to encapsulate all properties and behavior \nassociated with any single source of data, but the models have zero knowledge of how to load or persist data.  Don't worry \ntoo much about how these components work under the hood, we'll go over that in a future chapter.\n  \n\nMagic Graph models are composed of 4 main components:\n\n1. Property Definitions and base behavior \n2. Properties bundled into a Property Set \n3. The Model object\n4. Behavioral Strategies \n\n\n**Properties**\n  \nAt the core of every Magic Graph model, you will find a series of properties.  Much like a standard class property, \nMagic Graph properties have a name, a data type and a value.  Unlike standard class properties, Magic Graph properties\nare first class objects.  They fully encapsulate all behavior associated with their data type, are extensible, reusable, \nself-validating and have configurable behaviors.\n  \n\n**Property Set**\n\nThe model properties are bundled into a [Set-backed](https://github.com/SixArmDonkey/buffalotools_types#set) object called a [Property Set](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertySet.html) .\nThe property set provides methods for accessing property objects, their meta data, flags, configuration \ndata and the ability to add and remove properties at run time.\n  \n  \n**Model Objects**\n\nAll models must implement the [IModel](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-IModel.html) interface.  Magic Graph models\nare essentially wrappers for the property set, and they expose properties within the set as if they were public members of the model class.  Adding getter and \nsetter methods are optional, but recommended.  In addition to providing access to properties, models keep track of new and/or edited properties, have their own \nvalidation method, and can have additional behavioral strategy objects coupled to them.  \n\n  \n**Behavioral Strategies**\n\nStrategies are programs that modify the behavior of a model or property, and implement the [INamedPropertyBehavior](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-INamedPropertyBehavior.html) interface  Strategies are passed to the model during object construction, \nand models will call the strategy methods.  For example, say you had an order object, and you \nwanted to send the customer a receipt after they submit an order.  A strategy could be created that sends an email after\nthe order is successfully created and saved.  Both IModel and INamedPropertyBehavior can be extended to add additional \nevents as necessary.\n  \n---\n  \n\n## Getting Started\n  \n### Hello Model\n\nThis is one of many ways to write models in Magic Graph.  As you read through the documentation, we will gradually \nshift towards writing more robust and extensible models.  The following model example is used to illustrate the internal \nstructure of models.\n\nFor now, let's take a look at some basic model creation code.\n\nIn this example, the following objects are used:  \n[buffalokiwi\\magicgraph\\DefaultModel](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-DefaultModel.html)  \n[buffalokiwi\\magicgraph\\property\\DefaultIntegerProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-DefaultIntegerProperty.html)  \n[buffalokiwi\\magicgraph\\property\\DefaultStringProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-DefaultStringProperty.html)  \n[buffalokiwi\\magicgraph\\property\\PropertyListSet](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-PropertyListSet.html)  \n  \n  \nFirst step is to decide the names and data types of the properties to be included within the model.  In our example, we \nwill add two properties:  An integer property named \"id\", and a string property named \"name\".  We will use \nDefaultIntegerProperty, and DefaultStringProperty.  To create the model, each property is passed to the PropertyListSet \nconstructor, which is then passed to DefaultModel.  \n\n\n```php\n$model = new DefaultModel(                //..Create the model\n  new PropertyListSet(                    //..Create the property set \n    new DefaultIntegerProperty( 'id' ),   //..Add the id property\n    new DefaultStringProperty( 'name' )   //..Add the name property \n));\n```\n\nA model with two properties has now been created.  The properties are now available as public class properties.\n\n```php\n//..Set the id and name property values \n\n$model-\u003eid = 1;       \n$model-\u003ename = 'Hello Model';\n\n//..Get the id and property values \nvar_dump( $model-\u003eid ); //..Outputs: \"int 1\"\nvar_dump( $model-\u003ename ); //..Outputs: \"string 'Hello Model' (length=11)\"\n```\n\nNow, what happens if we try to assign a value of the wrong type to one of the properties?  An exception is thrown!\nThe following code will result in a [ValidationException](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-ValidationException.html) being thrown with the message: \"Value foo for property id must be an integer. Got string.\".\n\n```php\n$model-\u003eid = 'foo'; //..id is not a string.\n```\n\nModels are self-validating, and ValidationException will be thrown immediately when attempting to set an invalid value.  There are many validation \noptions attached to the various default properties included with Magic Graph, which we will cover in the [Validation](#) chapter.\n  \n---\n\n### Basic Database and Repository Setup\n  \nSo, what if we want to persist this data in a MySQL database?  Without going into too much detail, we can create a \nSQL repository, which doubles as an object factory for the above-defined model.\n\nThe following objects are used:\n\n[buffalokiwi\\magicgraph\\pdo\\IConnectionProperties](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-IConnectionProperties.html)  \nDefines connection properties used to establish a database connection  \n  \n[buffalokiwi\\magicgraph\\pdo\\IDBConnection](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-IDBConnection.html)  \nDefines a generic database connection  \n  \n[buffalokiwi\\magicgraph\\pdo\\MariaConnectionProperties](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-MariaConnectionProperties.html)  \nMariaDB/MySQL connection properties  \n  \n[buffalokiwi\\magicgraph\\pdo\\MariaDBConnection](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-MariaDBConnection.html)  \nA database connection and statement helper library for MariaDB/MySQL  \n  \n[buffalokiwi\\magicgraph\\pdo\\PDOConnectionFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-PDOConnectionFactory.html)  \nA factory for creating database connection instances    \n  \n  \nFirst step is to create a database connection.\n\n```php\n$dbFactory = new PDOConnectionFactory(         //..A factory for managing and sharing connection instances \n  new MariaConnectionProperties(  //..Connection properties for MariaDB / MySQL\n    'localhost',                  //..Database server host name \n    'root',                       //..User name\n    '',                           //..Password\n    'testdatabase' ),             //..Database \n  //..This is the factory method, which is used to create database connection instances\n  //..The above-defined connection arguments are passed to the closure.\n  function( IConnectionProperties $args  ) { \n    //..Return a MariaDB connection \n    return new MariaDBConnection( $args );\n  });\n```\n\nNext step is to create a table for our test model:\n  \n```sql\nCREATE TABLE `inlinetest` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(255) NOT NULL DEFAULT '',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 \n```\n  \nFinally, we create an instance of InlineSQLRepo, which is a repository for handling model construction, loading and saving data.\nYou may notice that we are now using a PrimaryIntegerProperty instead of an IntegerProperty for id.  This is because\nrepositories require at least one property to be flagged as a primary key, and PrimaryIntegerProperty automatically sets that flag.  \n  \n\n```php\n$repo = new InlineSQLRepo( \n  'inlinetest',                       //..Database table name \n  $dbFactory-\u003egetConnection(),        //..Database connection\n  //..Model properties follows \n  PrimaryIntegerProperty( 'id' ),     //..Primary id property \n  DefaultStringProperty( 'name' ));   //..Optional string property \n```\n\nNow we create and save!\n\nCreate a new model from our new repository like this:\n\n```php\n$model = $repo-\u003ecreate();\n```\n  \nWe can also initialize properties with the create method:\n  \n```php\n$model = $repo-\u003ecreate(['name' =\u003e 'foo']));\n```\n  \nSet the property values\n\n```php\n$model-\u003ename = 'foo';\n```\n  \nSince id is defined as a primary key, we do not want to set that value.  The repository will take care of assigning that for us.\nSave the model by passing it to the repository save() method.  \n\n```php\n$repo-\u003esave( $model );\n\necho $model-\u003eid;  //..Prints 1 \n```\n\nWhen saving, the repository first validates the model by calling the validate() method attached to the model.  Then, on a successful\nsave, the repository will assign the id (automatically generated by the database) to the id property.\n\n\nAssuming the id of the newly created record was 1, we can retrieve the model:\n\n```php\n$model = $repo-\u003eget('1');\n```\n\nThe getting started section shows the most basic way of working with Magic Graph.  While that's nice and all, \nit's pretty useless for anything other than a simple program.  The next several chapters will detail how to use Magic Graph\nin larger applications.\n\n---\n  \n## Property Configuration\n\nProperty configuration files are a way to define properties and property-specific behavior, and must implement the [IPropertyConfig](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyConfig.html) interface.\nThe configuration objects are similar to PHP traits, where we define partial objects.  These objects can be assigned to IModel\ninstances and define the property set (properties used within) and behavior of the associated model.\n\nIn the following example, we will create a sample property set with two properties: \"id\" and \"name\".  \n\nId will be an integer property, have a default value of zero, be flagged as a primary key, and will read only if the value is non-zero.  \nName will be a string property, have a default value of an empty string and be flagged as required.\n\nIn this example, these additional classes and interfaces are used:  \n  \nThe base property configuration is the base class used when defining property configurations.  It provides\nconstants, common property configurations and several methods for working with behaviors.\n[buffalokiwi\\magicgraph\\property\\BasePropertyConfig](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-BasePropertyConfig.html)  \n  \nIPropertyFlags defines various flags available to properties.  This interface can be extended to add additional flags and functionality.\n[buffalokiwi\\magicgraph\\property\\IPropertyFlags](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyFlags.html)  \n  \nIPropertyType defines the available property types.  Each type maps to a property object via the [IConfigMapper](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IConfigMapper.html) interface.  \n[buffalokiwi\\magicgraph\\property\\IPropertyType](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyType.html)  \n  \nStandardPropertySet uses the default IConfigMapper and [IPropertyFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyFactory.html) implementations to provide an \neasy way to instantiate IPropertySet instances when creating IModel instances.\n[buffalokiwi\\magicgraph\\property\\StandardPropertySet](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-StandardPropertySet.html)  \n  \n  \n```php\nclass SamplePropertyConfig extends BasePropertyConfig\n{\n  //..Returns an array detailing the properties to add\n  protected function createConfig() : array\n  {\n    //..A map of property name to configuration \n    return [\n      //..The Id Property \n      'id' =\u003e [\n        self::TYPE =\u003e IPropertyType::TINTEGER,     //..The data type\n        self::FLAGS =\u003e [IPropertyFlags::PRIMARY],  //..Flags \n        self::VALUE =\u003e 0                           //..Default value \n      ],\n        \n      'name' =\u003e [\n        self::TYPE =\u003e IPropertyType::TSTRING,\n        self::FLAGS =\u003e [IPropertyFlags::REQUIRED],\n        self::VALUE =\u003e ''\n      ]        \n    ];\n  }\n}\n```\n\nA property configuration object descends from BasePropertyConfig and/or implements the IPropertyConfig instance.\nOnly a single method createConfig() is required to be implemented in the descending class, and must return an \narray with zero or more property definitions.\n  \ncreateConfig() returns a map of property name to property configuration data.  When defining the property confuration, \n'type' (BasePropertyConfig::TYPE) is the only required attribute.  \n  \nBasePropertyConfig::FLAGS maps to an array, which contains constants from IPropertyFlags.  Zero or more flags may be supplied, and each will\nmodify how a property is validated.   \n\nDefault values can be set with the BasePropertyConfig::VALUE attribute, and assigning the value as the desired default value.\n  \n  \nAfter creating the property definitions, we can then assign them to a property set, which is assigned to a model.  Multiple\nIPropertyConfig instances can be passed to a StandardPropertySet.\n  \n```php  \n$model = new DefaultModel( new StandardPropertySet( new SamplePropertyConfig()));\n```\n  \nBasePropertyConfig contains a few helper constants, which can be used to simplify the creation of property configuration objects.\nFor example, the previous example could be rewritten as:\n  \n```php\nclass SamplePropertyConfig extends BasePropertyConfig\n{\n  //..Returns an array detailing the properties to add\n  protected function createConfig() : array\n  {\n    //..A map of property name to configuration \n    return [\n      //..The Id Property \n      'id' =\u003e self::FINTEGER_PRIMARY,\n      'name' =\u003e self::FSTRING_REQUIRED\n    ];\n  }\n}\n```\n  \nFINTEGER_PRIMARY will create an integer property, flagged as a primary key, with a default value of zero  \nFSTRING_REQUIRED will create a string property, flagged as required, with a default value of an empty string.  \n  \n  \n---\n  \n    \n### Property Configuration Array Attributes  \n  \nThe BasePropertyConfig class contains a series of constants used within the array returned by createConfig() \nto create properties for models.  Certain attributes are for specific data types, and using them with other types will have no effect.\n  \n  \n#### Caption\nProperty caption/label to be used at the application level.  \nMagic Graph does not read this value for any specific purpose.  \n```\nBasePropertyConfig::CAPTION = 'caption'\n```   \n  \n#### Id \nAn optional unique identifier for some property.  This is simply a tag, and is to be used at the application level.\nMagic Graph does not read this value for any specific purpose.\n```\nBasePropertyConfig::ID = 'id'\n```   \n  \n#### Default Value \nDefault value.  \nIf no value is supplied during model construction, or if the IProperty::reset() method is called, property value will be \nassigned to the default value listed in the property configuration object.\n```\nBasePropertyConfig::VALUE = 'value'\n```  \n  \n#### Setter Callback\nWhen a property value is set, any supplied setters will be called in the order in which they were defined.  \nEach property can define a single setter within the configuration array, but multiple setters can be added by \nsupplying property behavior objects to the property configuration object constructor.  \n  \nSetter callbacks are called by IProperty::setValue(), and can be used to modify an incoming value prior to \nvalidation.  When chaining setters, the result of the previous setter is used as the value argument for the subsequent \nsetter.  \n  \n```\nf( IProperty, mixed $value ) : mixed  \nBasePropertyConfig::SETTER = 'setter'\n```  \n  \n#### Getter Callback\nWhen a property value is retrieved, any supplied getters will be called in the order in which they were defined. \nEach property can define a single getter within the configuration array, but multiple getters can be added by \nsupplying property behavior objects to the property configuration object constructor.  \n  \nGetter callbacks are called by IProperty::getValue(), and can be used to modify a value prior to being returned by\ngetValue().  When chaining getters, the result of the previous getter is used as the value argument for the subsequent\ngetter.  \n```\nf( IProperty, mixed $value ) : mixed   \nBasePropertyConfig::GETTER = 'getter'\n```  \n  \n#### Model Setter Callback \nModel setters are the same as property setters, but they are called at the model level.  The difference\nbetween a model setter and a property setter is that model setters have access to other properties, and property \nsetters do not.  Since full model validation is only called on save, this can be used to validate state within an \nobject, and prevent any modifications by throwing a ValidationException.  \n  \n1. When calling IModel::setValue (or setting a value via IModel::__set()), model setters are called in the order in which they were defined.  \n2. Model setters are called prior to property setters and prior to property validation.\n3. When chaining model setters, the result of the previous setter is used as the value argument for the subsequent model setter.\n  \n```\nf( IModel, IProperty, mixed $value ) : mixed  \nBasePropertyConfig::MSETTER = 'msetter'\n```  \n  \n#### Model Getter Callback \nModel getters are the same as property getters, but they are called at the model level.  The difference\nbetween a model getter and a property getter is that model getters have access to other properties, and property \ngetters do not.  \n  \n1. When calling IModel::getValue (or getting a value via IModel::__get()), model getters are called in the order in which they were defined.  \n2. Model getters are called after property getters.\n3. When chaining model getters, the result of the previous getter is used as the value argument for the subsequent model getter.\n  \n```\nf( IModel, IProperty, mixed $value ) : mixed   \nBasePropertyConfig::MGETTER = 'mgetter'\n```  \n  \n#### Property Data Type\nThis must map to a valid value of [IPropertyType](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyType.html).\nFor more information see the [Property Data Types](#property-data-types) section.  \n```\nBasePropertyConfig::TYPE = \"type\"  \n```  \n    \n#### Property Flags \nThis must map to a comma-delimited list of valid [IPropertyFlags](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyFlags.html) values.  \nFor more information see the [Property Flags](#property-flags) section.  \n```\nBasePropertyConfig::FLAGS = \"flags\"  \n```  \n  \n#### Class name for properties returning objects \nWhen using properties backed by a descendant of ObjectProperty, the clazz attribute must be used.  The value should be a \nfully namespaced class name.  \n```\nBasePropertyConfig::CLAZZ = \"clazz\"\n```  \nFor example, when the property type is defined as Enum or Set, clazz would equal some enum class name.\n```php\n//..Sample enum class\nclass SampleEnum extends Enum {} \n\n//..Property configuration\n'enum_property' =\u003e [\n  'type' =\u003e 'enum',\n  'clazz' =\u003e SampleEnum::class\n]\n```  \n  \n  \n#### Initialize Callback  \nWhen IProperty::reset() is called, this function is called with the default value.  This is a way to modify the default\nvalue prior to it being assigned as the initial property value.  The value returned by the init callback is the \nnew default value.  \n```\nf( mixed $defaultValue ) : mixed  \nBasePropertyConfig::INIT = \"initialize\"\n```  \n  \n#### Minimum value/length  \nThis is used with both Integer and String properties, and is the minimum value or minimum string length.\n```\nBasePropertyConfig::MIN = \"min\"\n```    \n  \n#### Maximum value/length  \nThis is used with both Integer and String properties, and is the maximum value or minimum string length.\n```\nBasePropertyConfig::MAX = \"max\"\n```  \n  \n#### Validation\nValidate callbacks are for validating individual property values prior to save or when IProperty::callback() is called. \nValidate callbacks are called prior to the backing property object validation call, and can either return a boolean representing\nvalidity, or throw a ValidationException.  Returning false will automatically throw a ValidationException with an appropriate message.    \n```\n[bool is valid] = function( IProperty, [input value] )  \nBasePropertyConfig::VALIDATE = \"validate\"\n```  \n  \n#### Regular Expressions\nWhen using string properties, the \"pattern\" attribute can be used to supply a regular expression, which will be used during\nproperty validation.  Only values matching the supplied pattern can be committed to the property.\n```\nBasePropertyConfig::PATTERN = \"pattern\"\n```  \n  \n#### Custom configuration data\nA config array.  This is implementation specific, and is currently only used with Runtime Enum data types (IPropertyType::RTEnum). \nThis can be used for whatever you want within your application.\n```\nBasePropertyConfig::CONFIG = \"config\"\n```  \n  \n#### Embedded model prefix \nA prefix used by the default property set, which can proxy a get/set value call to a nested IModel instance.\nFor example, say you had a customer model, and wanted to embed an address inside.  Instead of copy/pasting properties or \nlinking the customer to addresses, you can assign a prefix to a property named 'address' in the customer configuration, and\nadd a CLAZZ property containing the class name of the address model.  The customer model will then embed the address model\ninside of the customer model, and all address model functionality will be included.  Furthermore, each address property \nwill appear to be a member of the customer model, and have the defined prefix.\n```\nBasePropertyConfig::PREFIX = 'prefix'\n\n//..Example configuration entry:\n'address' =\u003e [\n  'type' =\u003e IPropertyType::TMODEL,\n  'clazz' =\u003e Address::class,\n  'prefix' =\u003e 'address_'\n]  \n```  \n  \n#### On change event  \nAfter a property value is successfully set, change events will be called in the order in which they were supplied. \n```\nf( IProperty, oldValue, newValue ) : void   \nBasePropertyConfig::CHANGE = 'onchange'\n```  \n  \nFor a given property, create an [htmlproperty\\IElement](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-htmlproperty-IElement.html) instance used as an html form input.\nBasically, generate an html input for a property and return that as a string, which can be embedded in some template.  \n```\nf( IModel $model, IProperty $property, string $name, string $id, string $value ) : IElement   \nBasePropertyConfig::HTMLINPUT = 'htmlinput'\n```  \n\n#### Empty check  \nThis is an optional callback that can be used to determine if a property can be considered \"empty\".  The result \nof the supplied function is the result of an empty check.\n```\nf( IProperty, value ) : bool  \nBasePropertyConfig::IS_EMPTY = 'isempty'\n```\n  \n#### Tagging \nAn optional tag for the attribute.  \nThis can be any string, and is application specific.  Nothing in Magic Graph will operate on this value by default.  \n```\nBasePropertyConfig::TAG = 'tag'\n```  \n  \n---\n  \n  \n### Property Data Types\n  \nProperty data type definitions define which data type object a property is backed by.  All of the available definitions\nare within the the [buffalokiwi\\magicgraph\\property\\IPropertyType](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyType.html) interface.   \n  \nHere is a list of the built in property types that ship with Magic Graph:\n  \n#### Boolean  \nThe 'bool' property type will be backed by an instance of [IBooleanProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IBooleanProperty.html). \nUnless specified as null, boolean properties will have a default value of false.\n```\nIPropertyType::TBOOLEAN = 'bool'\n```\n  \n#### Integer  \nBacked by [IIntegerProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IIntegerProperty.html)\n\n```\nIPropertyType::TINTEGER = 'int'\n```\n  \n#### Decimal  \nBacked by [IFloatProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IFloatProperty.html)\n```\nIPropertyType::TFLOAT = 'float'\n```   \n  \n#### String  \nBacked by [IStringProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IStringProperty.html)\n```\nIPropertyType::TSTRING = 'string'\n```  \n  \n#### Enum  \nBacked by [IEnumProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IEnumProperty.html)\nColumn must list a class name implementing the IEnum interface in the 'clazz' attribute.  For more information see [BuffaloKiwi Types](https://github.com/SixArmDonkey/buffalotools_types).\n```\nIPropertyType::TENUM = 'enum'\n```  \n  \n#### Runtime Enum  \nBacked by [IEnumProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IEnumProperty.html)\nEnum members are configured via the \"config\" property and is backed by a RuntimeEnum instance.  Runtime Enum instances\ndo not use the \"clazz\" attribute. For more information see [BuffaloKiwi Types](https://github.com/SixArmDonkey/buffalotools_types).\n```\nIPropertyType::TRTENUM = 'rtenum' \n```  \n  \n#### Array  \nBacked by [ArrayProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-ArrayProperty.html)\nArray properties are mostly used by Magic Graph relationship providers.  While it's possible to define array properties for \narbitrary data, it is recommended to create a relationship or model service provider to manage the data contained within \narray properties.  \nArray properties can read the \"clazz\" argument to restrict the array members to objects of the specified type.\n```\nIPropertyType::TARRAY = 'array'\n```  \n  \n#### Set  \nSet properties are backed by [ISetProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-ISetProperty.html), and \nwill read/write instances of ISet (or descendants of ISet as specified by the \"clazz\" attribute).  For more information see [BuffaloKiwi Types](https://github.com/SixArmDonkey/buffalotools_types).\n```\nIPropertyType::TSET = 'set'\n```  \n  \n#### Date/Time  \nBacked by [IDateProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IDateProperty.html),\n and can be used to represent a date and/or time.  This would commonly be used with timestamp or DateTime SQL column types.  \n```\nIPropertyType::TDATE = 'date'\n```  \n  \n#### Currency.\nA property backed by [IMoneyProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IMoneyProperty.html), \ncontaining an object implementing the [IMoney](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-money-IMoney.html) interface.\nThis property type requires use of an service locator and have the MoneyPHP/Money dependency installed.  \n```\nIPropertyType::TMONEY = 'money'\n```  \n  \n#### IModel  \nBacked by [IModelProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-IModel.html) and contains an object\nimplementing the IModel interface.  Model properties are commonly managed by a [OneOnePropertyService](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-OneOnePropertyService.html).\n```\nIPropertyType::TMODEL = 'model'\n```  \n\n#### Object   \nA property that only accepts instances of a specified object type.  \nIt is recommended to extend the ObjectProperty class to create properties that handle specific object types instead of\nusing the generic ObjectProperty object.  In the future, I may mark ObjectProperty as abstract to prevent direct instantiation.  \n```\nIPropertyType::TOBJECT = 'object'\n```  \n  \n---\n  \n  \n### Property Flags \n  \nProperty Flags are a series of modifiers for properties.  Zero or more flags may be assigned to any property, and each \nwill modify the validation strategy used within the associated model.  Each flag is a constant defined within the \n[buffalokiwi\\magicgraph\\property\\IPropertyFlags](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyFlags.html) interface.  \n  \n  \n  \n#### No Insert\nThis property may never be inserted\n```\nIPropertyFlags::NO_INSERT = 'noinsert';\n```  \n  \n#### No Update\nThis property may never be updated.  \nThis can also be considered as \"read only\".  \n```\nIPropertyFlags::NO_UPDATE = 'noupdate'\n```  \n  \n#### Required \nThis property requires a value  \n```\nIPropertyFlags::REQUIRED = 'required'\n```  \n  \n#### Allow Null\nProperty value may include null  \n```\nIPropertyFlags::USE_NULL = 'null'\n```  \n  \n#### Primary Key \nPrimary key (one per property set)  \n```\nIPropertyFlags::PRIMARY = 'primary'\n```  \n  \n#### Sub config\nMagic Graph does not use this flag, but it is here in case some property is loaded from some sub/third \nparty config and you want to do something with those.  For example, this is used in Retail Rack to identify properties\nloaded from configurations stored within a database.\n```\nIPropertyFlags::SUBCONFIG = 'subconfig'\n```  \n\n#### Write Empty   \nCalling setValue() on the model will throw a ValidationException if the stored value is not empty.  \n```\nIPropertyFlags::WRITE_EMPTY = 'writeempty'\n```  \n  \n#### No Array Output\nSet this flag to prevent the property from being printed during a call to IModel::toArray().  toArray() is used \nto copy and save models, and not all properties should be read.  ie: the property connects to some api on read and the \nreturned value should not be saved anywhere.  \n```\nIPropertyFlags::NO_ARRAY_OUTPUT = 'noarrayoutput'\n```  \n  \n  \n---\n  \n  \n#### Property Behavior\n\nEach property has a series of callbacks as previously defined in [Property Configuration Array Attributes](#property-configuration-array-attributes). \nWhen creating instances of objects descending from [BasePropertyConfig](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-BasePropertyConfig.html), \nit is possible to pass instances of [INamedPropertyBehavior](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-INamedPropertyBehavior.html) \nto the constructor.  \n  \nThe purpose of this is to create different strategies for an object.  Strategies are independent, self-contained, and\ntestable programs.  Zero or more strategies may be attached to a property configuration object, may modify \nproperties of the associated model, and may cause side effects.  \n  \nFor example, say you wanted to add a debug message to a log file when a model was saved in your development environment. \nWe can create a class that extends [GenericNamedPropertyBehavior](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-GenericNamedPropertyBehavior.html), \nand overrides the getAfterSaveCallback() method.  \n\n  \n```php\n/**\n * Attach this strategy to any model to add a debug log message when the model is saved.\n */\nclass DebugLogSaveStrategy extends GenericNamedPropertyBehavior\n{\n  /**\n   * The log \n   * @var LoggerInterface\n   */\n  private LoggerInterface $log;\n  \n  \n  /**\n   * @param LoggerInterface $log\n   */\n  public function __construct( LoggerInterface $log )\n  {\n    //..Since this is a save event, we simply pass the name of the class as the property name.\n    //..Save events are called regardless of the supplied name.\n    parent::__construct( static::class );   \n    $this-\u003elog = $log;\n  }\n  \n  /**\n   * Retrieve the after save function  \n   * @return Closure|null function \n   */\n  public function getAfterSaveCallback() : ?Closure\n  {\n    return function( IModel $model ) : void {\n      //..Get the primary key value from the model\n      $priKey = $model-\u003egetValue( $model-\u003egetPropertySet()-\u003egetPrimaryKey()-\u003egetName());\n      \n      //..Add the log message \n      $this-\u003elog-\u003edebug( 'Model with primary key value: ' . $priKey . ' successfully saved.' );\n    };\n  }  \n}\n```\n  \nAfter creating our strategy, we can attach it to a model via it's property configuration object.  \n  \n```php\n//..Create the property config object and attach the strategy \n$config = new SamplePropertyConfig( new DebugLogSaveStrategy( new LoggerInterfaceImpl()));\n\n//..Create a model using the configuration \n$model = new DefaultModel( new StandardPropertySet( $config ));\n```\n  \nWhen the model is saved via some [IRepository](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-IRepository.html) instance, the \nafter save callback will be executed in the strategy, and the log message will be added.\n  \nThere are several callbacks, which together can be used to create rich models by using decoupled strategy objects.  \nSee [Property Configuration Array Attributes](#property-configuration-array-attributes) for details.\n\n  \nAdding behavioral strategies for individual properties is the same process as the above, except we would expose the \n\"name\" argument from the GenericNamedPropertyBehavior constructor.  \n  \n```php\n/**\n * Attach this strategy to a model to print a log message when a value was set \n */\nclass DebugSetterStrategy extends GenericNamedPropertyBehavior\n{\n  /**\n   * The log \n   * @var LoggerInterface\n   */\n  private LoggerInterface $log;\n  \n  \n  /**\n   * @param string $name Property name \n   * @param LoggerInterface $log\n   */\n  public function __construct( string $name, LoggerInterface $log )\n  {\n    //..Pass the property name \n    parent::__construct( $name );   \n    $this-\u003elog = $log;\n  }\n  \n  \n  /**\n   * Callback used to set a value.\n   * This is called prior to IProperty::validate() and the return value will \n   * replace the supplied value.\n   * \n   * f( IProperty, value ) : mixed\n   * \n   * @return Closure callback\n   */\n  public function getSetterCallback() : ?Closure\n  {\n    return function( IProperty $prop, $value ) {\n      //..Add the log message\n      $this-\u003elog-\u003edebug( $prop-\u003egetName() . ' changed to ' . (string)$value );\n\n      //..Return the unmodified value.\n      //..Setters can modify this value if desired\n      return $value;\n    };\n  }  \n}\n```\n  \nThen to use the strategy:  \n  \n```php\n//..Create the property config object and attach the strategy for the \"name\" property\n$config = new SamplePropertyConfig( new DebugSetterStrategy( 'name', new LoggerInterfaceImpl()));\n\n//..Create a model using the configuration \n$model = new DefaultModel( new StandardPropertySet( $config ));\n```\n  \nNow any time the \"name\" property is set, the debug log will show \"[Property name] changed to [new value]\"\n  \n  \n\n---\n  \n  \n#### Quick Models\n  \n[Quick models](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-QuickModel.html) can be useful \nwhen you need to create a temporary model, or if you need to quickly create a mock model.\nQuick models accept standard configuration arrays, and can do anything a standard model can do.  In the following example,\nwe create a model with two properties: \"id\" and \"name\", and we add some behavior to the name property. \nWhen setting the name property, \"-bar\" is appended to the incoming value.  When retrieving the name property, \n\"-baz\" is appended to the outgoing value.  \n  \n\n```php\n//..Create a new quick model \n$q = new \\buffalokiwi\\magicgraph\\QuickModel([\n  //..Id property, integer, primary key\n  'id' =\u003e [\n    'type' =\u003e 'int',\n    'flags' =\u003e ['primary']\n  ],\n    \n  //..Name property, string \n  'name' =\u003e [\n    'type' =\u003e 'string',\n      \n    //..Append -bar to the name property value when seting \n    'setter' =\u003e function( IProperty $prop, string $value ) : string {\n      return $value . '-bar';\n    },\n            \n    //..Append -baz to the name property value when retrieving \n    'getter' =\u003e function( IProperty $prop, string $value ) : string {\n      return $value . '-baz';\n    }\n  ]\n]);\n\n//..Set the name attribute\n$q-\u003ename = 'foo';\n\necho $q-\u003ename; //..Outputs \"foo-bar-baz\"\n```\n  \n\n#### Annotations\n\nPHP 8 added a wonderful new feature called attributes.  These snazzy things let us tag properties with things like the \nbacking object type, default values, flags, etc.  If you are willing to allow Magic Graph to make some assumptions, you can \nskip making property sets and configuration arrays/files.  An annotated model would look something like this:\n\n\n```php\n\n\nuse buffalokiwi\\magicgraph\\AnnotatedModel;\nuse buffalokiwi\\magicgraph\\property\\annotation\\IntegerProperty;\nuse buffalokiwi\\magicgraph\\property\\annotation\\BooleanProperty;\nuse buffalokiwi\\magicgraph\\property\\annotation\\DateProperty;\nuse buffalokiwi\\magicgraph\\property\\annotation\\ArrayProperty;\nuse buffalokiwi\\magicgraph\\property\\annotation\\EnumProperty;\nuse buffalokiwi\\magicgraph\\property\\annotation\\FloatProperty;\nuse buffalokiwi\\magicgraph\\property\\annotation\\SetProperty;\nuse buffalokiwi\\magicgraph\\property\\annotation\\StringProperty;\nuse buffalokiwi\\magicgraph\\property\\annotation\\USDollarProperty;\n\n\nclass Test extends AnnotatedModel\n{  \n  #[IntegerProperty]\n  private int $id;\n  \n  #[BooleanProperty]\n  private bool $b;\n  \n  #[DateProperty('d', '1/1/2020')]\n  private IDateTime $d;  \n  \n  #[ArrayProperty('a','\\stdClass')]\n  private array $a;\n  \n  #[EnumProperty('e','\\buffalokiwi\\magicgraph\\property\\EPropertyType','int')]\n  private \\buffalokiwi\\magicgraph\\property\\EPropertyType $e;\n  \n  #[FloatProperty]\n  private float $f;\n  \n  #[SetProperty('set','\\buffalokiwi\\magicgraph\\property\\SPropertyFlags',['noinsert','noupdate'])]\n  private \\buffalokiwi\\buffalotools\\types\\ISet $set;\n  \n  #[USDollarProperty]\n  private buffalokiwi\\magicgraph\\money\\IMoney $money;\n  \n  #[StringProperty]\n  private string $str;\n  \n  public \\buffalokiwi\\magicgraph\\property\\IIntegerProperty $pubProp;\n  \n  public function __construct()\n  {\n    $this-\u003epubProp = new buffalokiwi\\magicgraph\\property\\DefaultIntegerProperty( 'pubProp', 10 );    \n\n    parent::__construct( new \\buffalokiwi\\magicgraph\\property\\QuickPropertySet([\n       'name' =\u003e [\n           'type' =\u003e 'string',\n           'value' =\u003e 'qp string'\n       ]\n    ]));\n  }\n}\n\n\n$a = new Test();\n\n$aa = $a-\u003ea;\n$aa[] = new \\stdClass();\n$a-\u003ea = $aa;\n\n$a-\u003eid = 22;\n$a-\u003eb = true;\n$a-\u003ed = '10/10/2020';\n$a-\u003ef = 1.123;\n$a-\u003eset-\u003eadd( 'primary' );\n$a-\u003estr = 'foo';\n$a-\u003ee-\u003esetValue( 'string' );\n$a-\u003epubProp-\u003esetValue( 11 );\n$a-\u003emoney = '3.50';\n\nvar_dump( $a-\u003etoArray(null, true, true));\n\nOutputs:\n\narray (size=11)\n  'name' =\u003e string 'qp string' (length=9)\n  'id' =\u003e int 22\n  'b' =\u003e int 1\n  'd' =\u003e \n    object(DateTimeImmutable)[644]\n      public 'date' =\u003e string '2020-10-10 00:00:00.000000' (length=26)\n      public 'timezone_type' =\u003e int 3\n      public 'timezone' =\u003e string 'UTC' (length=3)\n  'a' =\u003e \n    array (size=1)\n      0 =\u003e \n        object(stdClass)[701]\n  'e' =\u003e string 'string' (length=6)\n  'f' =\u003e float 1.123\n  'set' =\u003e string 'primary,noupdate,noinsert' (length=25)\n  'money' =\u003e string '3.50' (length=4)\n  'str' =\u003e string 'foo' (length=3)\n  'pubProp' =\u003e int 11\n\n\n```\n\nThe above example mixes php attributes, a configuration array and a public IProperty intance.  All three ways can be \nused to create models if you extend the AnnotatedModel class.\n\nIn a future release, the annotations package will be extended to include all available property configuration options and \nto configure relationships.\n\n\n---\n \n  \n## Repositories \n  \nMagic Graph repositories are an implementation of the [Repository Pattern](https://martinfowler.com/eaaCatalog/repository.html).  \nRepositories are an abstraction that encapsulates the logic for accessing some persistence layer.  Similar to a collection, \nrepositories provide methods for creating, saving, removing and retrieving IModel instances.  Repositories are object factories, and are designed to \nproduce a single object type.  However, in a SQL setting, repositories may work with multiple tables from a database to produce a single model instance.\nWhen creating aggregate repositories, it's your choice if you wish to create a single repository per table, or a single \nrepository that access several tables.  It's worth noting that a repository may reference other repositories that access \ndifferent persistence layers.\n  \nRepositories implement the [IRepository](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-IRepository.html) interface.\n  \n  \n### Mapping Object Factory\n  \nData mappers map data retrieved from some persistence layer to an IModel instance, and implement the [IModelMapper](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-IModelMapper.html) interface. \nIn Magic Graph, data mappers also double as object factories.  \n  \nThe [MappingObjectFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-MappingObjectFactory.html) is normally the base class\nfor all repositories, and implements the [IObjectFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-IObjectFactory.html) interface. \nThe mapping object factory is responsible for holding a reference to a data mapper and a property set defining some model, and using those references can create \ninstances of a single IModel implementation.  The create() method accepts raw data from the persistence layer, creates a new IModel instance, and maps the supplied\ndata to the newly created model.  \n\nIObjectFactory implementations should not directly access any persistence layer.  Instead, extend this interface and define\nan abstraction for accessing a specific type of persistence layer.  For example, in Magic Graph, there is a SQLRepository for \nworking with a MySQL database.  \n  \nNote: If you simply want an object factory for creating models, MappingObjectFactory can be directly instantiated.  \n  \n  \n  \n### Saveable Mapping Object Factory\n  \n[SaveableMappingObjectFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-SaveableMappingObjectFactory.html) is an abstract class extending IObjectFactory, \nimplements [ISaveableObjectFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ISaveableObjectFactory.html), and \nadds the ability to save an IModel instance.  All repositories in Magic Graph extend this class.  The saveable mapping \nobject factory adds the save() and saveAll() methods, which outlines the repository save event as follows:  \n  \n1. Test the supplied model matches the implementation of IModel managed by the repository.  This prevents a model of an incorrect type from being saved.\n2. Call the protected beforeValidate() method.  This can be used to prepare a model for validation in extending repositories.\n3. Validate the model by calling IModel::validate()\n4. Call the protected beforeSave() method.  This can be used to prepare a model for save.\n5. Call the protected saveModel() method.  This is required to be implemented in all extending repositories \n6. Call the protected afterSave() method.  This can be used to clean up anything after a save.  This method should not have side effects.\n  \n  \nCalling saveAll() is a bit different than the save method.  After testing the model types, the save process is split into three parts:  \n  \n1. For each supplied model, call beforeValidate(), validate() and beforeSave().\n2. For each supplied model, call saveModel()\n3. For each supplied model, call afterSave()\n  \n  \n  \n### SQL Repository\n  \nThe [SQLRepository](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-SQLRepository.html) is the \n[IRepository](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-IRepository.html), used for \nworking with MariaDB/MySQL databases.  The SQLRepository also extends the [ISQLRepository](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ISQLRepository.html) \ninterface, which adds additional methods for working with SQL databases.\n  \nIn the following example, we will be using the same table and database connection outlined in [Basic Database and Repository Setup](#basic-database-and-repository-setup).  \n  \nInstantiating a SQLRepository:  \n  \n```php\n$testSQLRepo = new SQLRepository(                            //..Create the SQL Repository\n  'inlinetest',                                              //..Table Name\n  new DefaultModelMapper( function( IPropertySet $props ) {  //..Create a data mapper \n    return new DefaultModel( $props );                       //..Object factory \n  }, IModel::class ),                                        //..Type of model being returned \n  $dbFactory-\u003egetConnection(),                               //..SQL database connection \n  new QuickPropertySet([                                     //..Property set defining properties added to the model \n    //..Id property, integer, primary key      \n    'id' =\u003e [                                                //..\"id\" is a property\n      'type' =\u003e 'int',                                       //..Id is an integer\n      'flags' =\u003e ['primary']                                 //..Id is the primary key\n    ], \n\n    //..Name property, string \n    'name' =\u003e [                                              //..\"name\" is a property \n      'type' =\u003e 'string',                                    //..Name is a string \n    ]\n  ])\n);\n```\n  \nThe above-setup is similar to the InlineSQLRepo, but it allows us much more fine-grained control over which components are \nused.  Here, we are able to define which data mapper is used and how the property set is created.  For more information, please\nsee [Extensible Models](#extensible-models).  \n  \n  \n  \n### Decorating Repositories\n  \nMagic Graph provides several proxy classes, which can be used as base classes for repository decorators.  \n  \n1. [ObjectFactoryProxy](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ObjectFactoryProxy.html)\n2. [RepositoryProxy](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-RepositoryProxy.html)\n3. [SaveableMappingObjectFactoryProxy](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-SaveableMappingObjectFactoryProxy.html)\n4. [ServiceableRepository](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ServiceableRepository.html)\n5. [SQLRepositoryProxy](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-SQLRepositoryProxy.html)\n6. [SQLServiceableRepository](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-SQLServiceableRepository.html)\n\nEach of the above-listed proxy classes accept the an associated repository instance as a constructor argument, and will map the method\ncalls to the supplied repository instance.  The proxy classes should be extended to provide additional functionality to a repository.  \nServiceableRepository and SQLServiceableRepository are implementations of proxy classes, and provide ways to further extend functionality of \nrepositories.  These are discussed in the next section.  \n  \nI plan on adding more decorators in a future Magic Graph release, and currently there is a single decorator included in the \ncurrent version:\n\n[CommonObjectRepo](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-CommonObjectRepo.html) extends the \nRepositoryProxy class, and is used to prevent multiple database lookups.  Each time a model is retrieved from the repository, it is cached in memory, and \nany subsequent retrieval calls will return the cached version of the model.  \n  \nHere's an example of how to use a decorator:\n\n```php\n$testSQLRepo = new CommonObjectRepo( new SQLRepository(      //..Create the SQL Repository and add the caching decorator \n  'inlinetest',                                              //..Table Name\n  new DefaultModelMapper( function( IPropertySet $props ) {  //..Create a data mapper \n    return new DefaultModel( $props );                       //..Object factory \n  }, IModel::class ),                                        //..Type of model being returned \n  $dbFactory-\u003egetConnection(),                               //..SQL database connection \n  new QuickPropertySet([                                     //..Property set defining properties added to the model \n    //..Id property, integer, primary key      \n    'id' =\u003e [                                                //..\"id\" is a property\n      'type' =\u003e 'int',                                       //..Id is an integer\n      'flags' =\u003e ['primary']                                 //..Id is the primary key\n    ], \n\n    //..Name property, string \n    'name' =\u003e [                                              //..\"name\" is a property \n      'type' =\u003e 'string',                                    //..Name is a string \n    ]\n  ])\n));\n```  \n  \nDecorating repositories is easy and fun!\n  \n  \n\n### Serviceable Repository \n  \nThe serviceable repositories are used for repository relationships, which are discussed in the [Relationships](#relationships) section.  \n  \nEssentially, serviceable repositories accept [ITransactionFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ITransactionFactory.html) \nand zero or more IModelPropertyProvider instances, which are used to extend the functionality of certain properties.  \n  \nFor example, say you have a model A with an IModel property B.  By default the repository for model A does not know \nrepository B or model B exists.  A IModelPropertyProvider instance could add a lazy loading scheme for retrieving \nmodel B when the property is accessed from model A.  Additionally, the model property provider could provide a way to \nsave edits to model B when model A is saved.  \n  \n  \n### Composite Primary Keys\n  \nMagic Graph fully supports composite primary keys, and certain methods of IRepository and ISQLRepository will contain variadic id arguments for passing multiple primary key values.\nComposite primary keys are assigned via the IPropertyFlags::PRIMARY attribute as follows:\n\n```php\n[                                                          \n  //..Id property, integer, primary key      \n  'id' =\u003e [                                                //..\"id\" is a property\n    'type' =\u003e 'int',                                       //..Id is an integer\n    'flags' =\u003e ['primary']                                 //..Id is the primary key\n  ], \n\n  'id2' =\u003e [                                               //..\"id2\" is a property\n    'type' =\u003e 'int',                                       //..Id2 is an integer\n    'flags' =\u003e ['primary']                                 //..Id2 is the other primary key\n  ], \n]\n```\n  \nNote: when supplying primary key values to repository methods, they are accepted in the order in which they were defined.\nI will create a way to not have to depend on the order of arguments in a future release.\n  \n  \n---\n  \n\n## Transactions\n\n### Transactions Overview \nTransactions represent some unit of work, and are typically used to execute save operations against some persistence layer.  Similar to a database transaction, \nMagic Graph transactions will:\n\n1. Start a transaction in the persistence layer when available\n2. Execute arbitrary code against the persistence layer\n3. Commit the changes\n4. Roll back the changes on failure\n\nTransactions are based on a single interface [ITransaction](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ITransaction.html), \nand can have multiple implementations used to support various persistence layers.  Magic Graph fully supports using \nmultiple, and different, persistence layers concurrently.  Transactions can be considered an adapter, which \nexecutes persistence-specific commands used to implement the required commit and rollback functionality.\n\nCurrently, Magic Graph ships with a single transaction type: [MySQLTransaction](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-MySQLTransaction.html)\n\n\n### Creating a Transaction\n\nAt the heart of any transaction is the code to be executed.  In Magic Graph, the interface [IRunnable](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-IRunnable.html)\nis used to define the code to be executed within a transaction.  This type exists, because each persistence type will require a subclass of IRunnable to be created.\nThese types are used to group transactions by persistence type, and to expose persistence-specific methods that may be required\nwhen working with the transactions.  For example, [ISQLRunnable](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ISQLRunnable.html) is used \nfor persistence layers that utilize SQL.  ISQLRunnable adds a single method getConnection(), which can be used to access the underlying database connection.  \n\nIn it's simplest form, a transaction is a function passed to a Transaction object.  The supplied function is executed \nwhen Transaction::run() is called.  It is worth noting that Transaction can accept multiple functions.  Transaction::run()\nwill call each of the supplied functions in the order in which they were received.\n\n\n```php\n\n//..Get some data, model, etc.\n$data = 'This represents a model or some other data being saved';\n\n//..Create a new transaction. and write the contents of $data to a file when Transaction::run() is executed.\n$transaction = new buffalokiwi\\magicgraph\\persist\\Transaction( new buffalokiwi\\magicgraph\\persist\\Runnable( function() use($data) {\n  file_put_contents( 'persistence.txt', $data );\n}));\n\n```\n\n\nExecuting the transaction \n\n```php\n\n//..Start a new transaction inside of the persistence layer \n$transaction-\u003ebeginTransaction();\n\ntry {\n  //..Execute the code \n  $transaction-\u003erun();\n\n  //..Commit any changes in the persistence layer \n  $transaction-\u003ecommit();\n\n} catch( \\Exception $e ) {\n  //..OH NO!  An Error!\n  //..Revert any changes in the persistence layer\n  $transaction-\u003erollBack();\n}\n\n```\n\nThe default Transaction object shipped with Magic Graph does not connect to any specific persistence layer, and the implementations\nof beginTransaction, commit and rollBack do nothing.\n\n\nSince we want methods to actually do things, this is an example of how to run a transaction against MySQL/MariaDB.  A MySQL \ntransaction is passed an instance of [ISQLRunnable](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ISQLRunnable.html).  Currently, \nthere is a single implementation of ISQLRunnable, and that is [MySQLRunnable](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-MySQLRunnable.html).  \nMySQL runnable differs from the Transaction object used in the previous example by adding a constructor argument that accepts an instance of ISQLRepository.  The repository is used to \nobtain an instance of [buffalokiwi\\magicgraph\\pdo\\IDBConnection](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-IDBConnection.html), which is used to execute \nthe transaction.\n\nThe following is a full example of how to execute a transaction against a SQLRepository instance:\n\n```php\n\n//..Create a repository\n$testSQLRepo = new SQLRepository(\n  'inlinetest',\n  new DefaultModelMapper( function( IPropertySet $props ) {\n    return new DefaultModel( $props );\n  }, IModel::class ),\n  $dbFactory-\u003egetConnection(),\n  new QuickPropertySet([\n    //..Id property, integer, primary key\n    'id' =\u003e [\n      'type' =\u003e 'int',\n      'flags' =\u003e ['primary']\n    ],\n\n    //..Name property, string \n    'name' =\u003e [\n      'type' =\u003e 'string',\n    ]\n  ])\n);   \n   \n//..Create a new model and assign some property values\n$model = $testSQLRepo-\u003ecreate([]);\n$model-\u003ename = 'test';\n  \n//..Create a transaction \n$transaction = new \\buffalokiwi\\magicgraph\\persist\\MySQLTransaction(\n  new \\buffalokiwi\\magicgraph\\persist\\MySQLRunnable(\n    $testSQLRepo,\n    function() use( $testSQLRepo, $model ) {\n      $testSQLRepo-\u003esave( $model );\n    }\n));\n\n\n//..Start a new transaction inside of the persistence layer\n$transaction-\u003ebeginTransaction();\n\ntry {\n  //..Execute the code \n  $transaction-\u003erun();\n\n  //..Commit any changes in the persistence layer \n  $transaction-\u003ecommit();\n\n} catch( \\Exception $e ) {\n  //..OH NO!  An Error!\n  //..Revert any changes in the persistence layer\n  $transaction-\u003erollBack();\n}\n\n```\n\nWhile transactions against a single persistence engine could be more simply coded directly against the database, \nthe Magic Graph Transaction abstraction provides a way for us to run transactions against multiple database connections, \nor even different persistence engines.  \n\n\n### Transaction Factory\n\nThe transaction factory generates instances of some subclass of ITransaction.  The idea is to pass ITransactionFactory::createTransactions() a list of \nIRunnable instances, and the transaction factory will then group them by persistence type (registered subclass).  \nTransactions are executed as follows:\n\n1. Begin transaction is executed for each ITransaction instance\n2. Run is called for each ITransaction instance\n3. Commit is called for each ITransaction Instance\n4. If an exception is thrown at any time, rollback is called for each ITransaction instance and the exception is rethrown.\n\n\n```php\n\n//..Create a database connection factory for some MySQL database\n$dbFactory = new PDOConnectionFactory( \n  new MariaConnectionProperties( \n    'localhost',    //..Host\n    'root',         //..User\n    '',             //..Pass\n    'retailrack' ), //..Database \n  function(IConnectionProperties $args  ) {\n    return new MariaDBConnection( $args );\n});\n\n\n//..Create a quick test repository for a table named \"inlinetest\", with two columns id (int,primary,autoincrement) and name(varchar).\n$repo = new InlineSQLRepo( \n  'inlinetest', \n  $dbFactory-\u003egetConnection(),\n  new PrimaryIntegerProperty( 'id' ),\n  new DefaultStringProperty( 'name' )\n);\n\n//..Create a new model and set the name property value to \"test\"\n$model = $repo-\u003ecreate([]);\n$model-\u003ename = 'test';\n\n\n//..Create a new transaction factory\n//..The supplied map is used within the TransactionFactory::createTransactions() method, and will generate ITransaction\n//  instances of the appropriate type based on a predefined subclass of IRunnable \n//..Instances passed to TransactionFactory must be ordered so that the most generic IRunnable instances are last.\n$tf = new TransactionFactory([\n  //..Supplying ISQLRunnable instances will generate instaces of MySQLTransaction\n  ISQLRunnable::class =\u003e function( IRunnable ...$tasks ) { return new MySQLTransaction( ...$tasks ); },\n  //..Supplying instances of IRunnable will generate a Transaction instance\n  IRunnable::class =\u003e function( IRunnable ...$tasks ) { return new Transaction( ...$tasks ); }\n]);\n\n//..Execute a mysql transaction\n//..This will use a database transaction to save the model\n//..If any exceptions are thrown by the supplied closure, then rollback is called.  Otherwise, commit is called \n//..upon successful completion of the closure\n$tf-\u003eexecute( new MySQLRunnable( $repo, function() use($repo, $model) {\n  $repo-\u003esave( $model );  \n}));\n\n```\n\n\nIf $repo-\u003esave() were to throw an exception in the previous example, then the transaction would have been rolled back.  If\nthe following code is executed, then you will see how the row is never added to the database due to rollback being called\nwhen the exception is thrown.\n\n```php        \n$tf-\u003eexecute( new MySQLRunnable( $repo, function() use($repo, $model) {\n  $repo-\u003esave( $model );  \n  throw new \\Exception( 'No save for you' );\n}));\n\n```\n\n\n---\n\n\n## Model Relationship Providers \n\nSimilar to a foreign key in a relational database, relationships allow us to create associations between domain objects.\nIn Magic Graph, a model (IModel) may contain zero or more properties that reference a single or list of associated IModel objects.\nThe parent model may contain [IModelProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-IModel.html)\nand/or [ArrayProperty](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-ArrayProperty.html) properties, \nwhich can hold referenced model objects.\n\nFor example, the following configuration array contains a model property and an array property.\n\n```php\n\n[\n  'one' =\u003e [\n      'type' =\u003e 'model',\n      'flags' =\u003e ['noinsert','noupdate'],\n      'clazz' =\u003e \\buffalokiwi\\magicgraph\\DefaultModel::class\n  ]\n\n  'many' =\u003e [\n      'type' =\u003e 'array',\n      'flags' =\u003e ['noinsert','noupdate'],\n      'value' =\u003e [],\n      'clazz' =\u003e \\buffalokiwi\\magicgraph\\DefaultModel::class\n  ]\n]\n\n```\n\nBoth Model and Array properties must include the \"clazz\" configuration property, which must equal the class name of the \nobject or objects in the array.  This is used to determine which object to instantiate within the relationship provider, and to \nensure that only objects of the specified type are accepted when setting the property value.\n\nNotice that both properties are marked with \"noinsert\" and \"noupdate\".  This is required for both model and array properties, \nand will prevent the properties from being used in insert and update database queries.  If these values are omitted, IModel \nproperties will persist as IModel::__toString() and ArrayProperty will be encoded as json.  \n\nAssigning the values to the parent model goes something like this:\n\n```php\n  //..Assuming $model was created using the above config and that $ref1 and $ref2 are both instances of DefaultModel\n\n  //..Ok\n  $model-\u003eone = $ref1;\n\n  //..Throws exception\n  $model-\u003eone = 'foo';\n\n  //..Multiple models can be added as an array\n  $model-\u003emany = [$ref1, $ref2];\n```\n\n\nOnce we have some model or array of models property, we may want to automate the loading and saving of those models.  For example,\nwhen accessing a model property, we can load the model from the database and return it.  We could also save any edits to the referenced\nmodel when the parent model is saved.  This behavior is accomplished through implementing \nthe [IModelPropertyProvider](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-IModelPropertyProvider.html) interface.\n\nThe IModelPropertyProvider defines several methods for initialization of a model, retrieving the value, setting the value and persisting the value.  \nModel property providers must be used with supporting models and repositories.  \n\n\n```php\n//..A sample child model.  This uses a unique class name instead of QuickModel because IModelProperty will attempt\n//  to instantiate an instance of the model when assigning the default value, and quick model is generic.\nclass ChildModel extends buffalokiwi\\magicgraph\\QuickModel {\n  public function __construct() {\n    parent::__construct([\n      'name' =\u003e [\n         'type' =\u003e 'string',\n         'value' =\u003e 'child model'\n       ]\n    ]);    \n  }\n}\n  \n//..The parent model includes a property \"child\", which is backed by an IModelProperty, and will contain \n//  an instance of ChildModel\n$parent = new buffalokiwi\\magicgraph\\QuickModel([\n   'name' =\u003e [\n       'type' =\u003e 'string',\n       'value' =\u003e 'parent model'\n   ],\n    \n   'child' =\u003e  [\n       'type' =\u003e 'model',\n       'clazz' =\u003e ChildModel::class\n   ]\n]);\n\n\n//..Models are converted to arrays when using toArray()\nvar_dump( $parent-\u003etoArray( null, true, true ));\n\nOutputs:\narray (size=2)\n  'name' =\u003e string 'parent model' (length=12)\n  'child' =\u003e \n    array (size=1)\n      'name' =\u003e string 'child model' (length=11)\n\n\n```\n\n\n\n### Serviceable Model\n\nA [Serviceable Model](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-ServiceableModel.html) extends the \n[DefaultModel](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-DefaultModel.html), and modifies the DefaultModel constructor \nto accept zero or more IModelPropertyProvider instances.  The passed providers are associated with properties defined within the parent model configuration, and \nwill handle loading and saving of the associated model(s).  \n\n\n### Serviceable Repository\n\n\n[Serviceable Repository](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ServiceableRepository.html) \nand [SQL Serviceable Repository](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-SQLServiceableRepository.html) (for SQL repositories)\nare repository decorators, which add support for IModelPropertyProviders.  When models are created in the repository object factory, the model property provider \ninstances are passed to the ServiceableModel constructor.  Additionally, when the repository save method is called, the model property providers save functions will be included \nas part of the save transaction.\n\n\n\nThe next section will describe the model property providers included with Magic Graph.\n\n\n---\n\n\n## Relationships\n\nSee [Model service providers](#model-relationship-providers) for information about model properties and IModelPropertyProvider.\n\nThe following tables are used in the One to One and One to Many example sections:\n\n```sql\n-- Parent Table\ncreate table table1 (\n  id int not null primary key auto_increment,\n  name varchar(20) not null,\n  childid int not null\n) engine=innodb;\n\n\n-- Child / linked table \ncreate table table2 (\n  id int not null auto_increment,\n  link_table1 int not null,\n  name varchar(20) not null,\n  primary key (link_table1, id),\n  key id(id)\n) engine=innodb;\n\n--Insert the parent model record\ninsert into table1 (name,childid) values ('Parent',1);\n\n--insert the child model records\ninsert into table2 (link_table1, name) values(last_insert_id(),'Child 1'),(last_insert_id(),'Child 2');\n```\n\n\n\n### One to One\n\nThe [OneOnePropertyService](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-OneOnePropertyService.html) provides\nthe ability to load, attach, edit and save a single associated model.  \n\n\n```php\n\n//..When using model property providers / relationships, models MUST extend ServiceableModel.  ServiceableModel \n//  extends DefaultModel, and adds the required functionality for relationships.\n//..Table1 Model\nclass Table1Model extends \\buffalokiwi\\magicgraph\\ServiceableModel {};\n\n//..Table2 Model\nclass Table2Model extends \\buffalokiwi\\magicgraph\\ServiceableModel {};\n\n\n//..Create a SQL Database connection \n$dbFactory = new buffalokiwi\\magicgraph\\pdo\\PDOConnectionFactory( //..A factory for managing and sharing connection instances \n  new buffalokiwi\\magicgraph\\pdo\\MariaConnectionProperties(       //..Connection properties for MariaDB / MySQL\n    'localhost',                  //..Database server host name \n    'root',                       //..User name\n    '',                           //..Password\n    'retailrack' ),               //..Database \n  //..This is the factory method, which is used to create database connection instances\n  //..The above-defined connection arguments are passed to the closure.\n  function( buffalokiwi\\magicgraph\\pdo\\IConnectionProperties $args  ) { \n    //..Return a MariaDB connection \n    return new buffalokiwi\\magicgraph\\pdo\\MariaDBConnection( $args );\n  }\n);\n\n\n//..Create the transaction factory\n$tFact = new \\buffalokiwi\\magicgraph\\persist\\DefaultTransactionFactory();\n\n\n//..Table2 Repository\n//..This must be initialized prior to Table1Repo because Table1Repo depends on Table2Repo\n$table2Repo = new buffalokiwi\\magicgraph\\persist\\DefaultSQLRepository(\n  'table2', \n  $dbFactory-\u003egetConnection(),\n  Table2Model::class,\n  $table2Properties\n);\n\n//..Create properties for Table1Model\n$table1Properties = new buffalokiwi\\magicgraph\\property\\QuickPropertySet([\n  //..Primary key\n  'id' =\u003e [\n      'type' =\u003e 'int',\n      'flags' =\u003e ['primary']\n  ],\n    \n  //..A name \n  'name' =\u003e [\n      'type' =\u003e 'string'      \n  ],\n    \n   //..Property containing the primary key for a Table2Model \n  'childid' =\u003e [\n      'type' =\u003e 'int',\n      'value' =\u003e 0\n  ],\n    \n  //..Child model property.\n  //..A model from Table2Repository is pulled by the id defined in the \"childid\" property\n  'child' =\u003e [\n    'type' =\u003e 'model',\n    'flags' =\u003e ['noinsert','noupdate','null'],  //..Since Table2Model requires constructor arguments, we'll pass null here.\n    'clazz' =\u003e Table2Model::class\n  ]\n]);\n\n\n$table1Repo = new \\buffalokiwi\\magicgraph\\persist\\DefaultSQLServiceableRepository(\n    'table1', //..SQL table name \n    $dbFactory-\u003egetConnection(), //..SQL database connection \n    Table1Model::class, //..The Table1Model class name used for the object factory \n    $table1Properties,  //..Properties used to create Table1Model instances\n    $tFact, //..Transaction factory used to handle saving across multiple model property providers \n    new \\buffalokiwi\\magicgraph\\OneOnePropertyService( new \\buffalokiwi\\magicgraph\\OneOnePropSvcCfg(\n      $table2Repo,\n      'childid',\n      'child'\n)));        \n\n//..Get the only record in table1\n$model = $table1Repo-\u003eget('1');\n\n//..Print the model contents with related child models \nvar_dump( $model-\u003etoArray( null, true, true ));\n\n\nOutputs:\narray (size=4)\n  'id' =\u003e string '1' (length=1)\n  'name' =\u003e string 'Parent' (length=6)\n  'childid' =\u003e string '1' (length=1)\n  'child' =\u003e \n    array (size=3)\n      'id' =\u003e string '1' (length=1)\n      'link_table1' =\u003e string '1' (length=1)\n      'name' =\u003e string 'Child 1' (length=7)\n```\n\n\n\n### One to Many\n\nThe [OneManyPropertyService](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-OneManyPropertyService.html) \nprovides the ability to load, attach, edit and save multiple associated models.  \n\n\n```php\n\n//..When using model property providers / relationships, models MUST extend ServiceableModel.  ServiceableModel \n//  extends DefaultModel, and adds the required functionality for relationships.\n//..Table1 Model\nclass Table1Model extends \\buffalokiwi\\magicgraph\\ServiceableModel {};\n\n//..Table2 Model\nclass Table2Model extends \\buffalokiwi\\magicgraph\\ServiceableModel {};\n\n\n//..Model properties for Table1\n$table1Properties = new buffalokiwi\\magicgraph\\property\\QuickPropertySet([\n  'id' =\u003e [\n      'type' =\u003e 'int',\n      'flags' =\u003e ['primary']\n  ],\n    \n  'name' =\u003e [\n      'type' =\u003e 'string'      \n  ],\n    \n  'children' =\u003e [\n    'type' =\u003e 'array',\n    'flags' =\u003e ['noinsert','noupdate'],\n    'clazz' =\u003e Table2Model::class\n  ]\n]);\n\n\n//..Model properties for table 2 \n$table2Properties = new buffalokiwi\\magicgraph\\property\\QuickPropertySet([\n  'id' =\u003e [\n      'type' =\u003e 'int',\n      'flags' =\u003e ['primary']\n  ],\n   \n  'link_table1' =\u003e [\n      'type' =\u003e 'int',\n      'value' =\u003e 0\n  ],\n    \n  'name' =\u003e [\n      'type' =\u003e 'string'      \n  ]\n]);\n\n//..Create a SQL Database connection \n$dbFactory = new buffalokiwi\\magicgraph\\pdo\\PDOConnectionFactory( //..A factory for managing and sharing connection instances \n  new buffalokiwi\\magicgraph\\pdo\\MariaConnectionProperties(       //..Connection properties for MariaDB / MySQL\n    'localhost',                  //..Database server host name \n    'root',                       //..User name\n    '',                           //..Password\n    'retailrack' ),               //..Database \n  //..This is the factory method, which is used to create database connection instances\n  //..The above-defined connection arguments are passed to the closure.\n  function( buffalokiwi\\magicgraph\\pdo\\IConnectionProperties $args  ) { \n    //..Return a MariaDB connection \n    return new buffalokiwi\\magicgraph\\pdo\\MariaDBConnection( $args );\n  }\n);\n\n\n//..Create the transaction factory\n$tFact = new \\buffalokiwi\\magicgraph\\persist\\DefaultTransactionFactory();\n\n\n//..Table2 Repository\n//..This must be initialized prior to Table1Repo because Table1Repo depends on Table2Repo\n$table2Repo = new buffalokiwi\\magicgraph\\persist\\DefaultSQLRepository(\n  'table2', \n  $dbFactory-\u003egetConnection(),\n  Table2Model::class,\n  $table2Properties\n);\n\n//..Table1 Repository\n//..A sql database repository that can include model property providers used for relationships\n$table1Repo = new \\buffalokiwi\\magicgraph\\persist\\DefaultSQLServiceableRepository( \n    'table1', //..SQL table name \n    $dbFactory-\u003egetConnection(), //..SQL database connection \n    Table1Model::class, //..The Table1Model class name used for the object factory \n    $table1Properties,  //..Properties used to create Table1Model instances\n    $tFact, //..Transaction factory used to handle saving across multiple model property providers \n    new buffalokiwi\\magicgraph\\OneManyPropertyService( //..This handles loading and saving related models \n      new buffalokiwi\\magicgraph\\OneManyPropSvcCfg( //..Configuration \n        $table2Repo,    //..Linked model repository //$parentIdProperty, $arrayProperty, $linkEntityProperty, $idProperty)\n        'id',           //..The parent model primary key property name.\n        'children',     //..The parent model property name for the array of linked models\n        'link_table1',  //..A linked model property that contains the parent id\n        'id' )          //..A linked model property containing the unique id of the linked model\n));\n\n\n//..Get the only record in table1\n$model = $table1Repo-\u003eget('1');\n\n//..Print the model contents with related child models \nvar_dump( $model-\u003etoArray( null, true, true ));\n\n\nOutputs:\n\narray (size=3)\n  'id' =\u003e string '1' (length=1)\n  'name' =\u003e string 'Parent' (length=6)\n  'children' =\u003e \n    array (size=2)\n      0 =\u003e \n        array (size=3)\n          'id' =\u003e string '1' (length=1)\n          'link_table1' =\u003e string '1' (length=1)\n          'name' =\u003e string 'Child 1' (length=7)\n      1 =\u003e \n        array (size=3)\n          'id' =\u003e string '2' (length=1)\n          'link_table1' =\u003e string '1' (length=1)\n          'name' =\u003e string 'Child 2' (length=7)\n```\n\n\n### Many to Many \n\nSometimes we have lots of things that can map to lots of other things.  For example, in can ecommerce setting, \nproducts may map to multiple categories, and categories may contain multiple products.  In this instance, we would \nrequire a junction table to store those mappings.  Thankfully, this is fairly easy in Magic Graph.\n\n\nFirst, we start with a standard junction table.  If we use the following table definition, we can use the built in \nmodels for a junction table.\n\n```sql\n\nCREATE TABLE `product_category_link` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `link_parent` int(11),\n  `link_target` int(11),\n  PRIMARY KEY (`link_parent`,`link_target`),\n  KEY `id` (`id`)\n) ENGINE=InnoDB \n\n```\n\n1. \"id\" contains the primary key\n2. \"link_parent\" is the id of the parent model.  ie: a product id\n3. \"link_target\" is the id of the target model.  ie: a category id\n\n\nNow we create two other tables.  One for parent and the other one for the target.  For fun, we'll add a name column to both.\n\n\n```sql \n\ncreate table `product` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(50) not null,\n  primary key (id)\n) ENGINE=InnoDB;\n\n\ninsert into product (name) values ('product1');\ninsert into product (name) values ('product2');\n\n```\n\n\nThe category table \n\n```sql \n\ncreate table `product_category` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(50) not null,\n  primary key (id)\n) ENGINE=InnoDB;\n\n\ninsert into product_category (name) values ('category1');\ninsert into product_category (name) values ('category2');\n\n```\n\nNow we add product1 to category1, and product2 to category2\n\n```sql\n\ninsert into product_category_link (link_parent,link_target) values (1,1),(2,2);\n\n```\n\n\n\n\n```php\n\n\n//..Define the category model\nclass CategoryModel extends \\buffalokiwi\\magicgraph\\ServiceableModel {}\n\n//..Create the category model property configuration \n//..In this instance, we are using QuickJunctionPropertyConfig because we want to use this model as a junction table target\n//..QuickJunctionPropertyConfig implements IJunctionTargetProperties, which exposes the primary id property name and is used \n//  to generate database queries.\n$cProps = new \\buffalokiwi\\magicgraph\\property\\QuickPropertySet( new \\buffalokiwi\\magicgraph\\junctionprovider\\QuickJunctionPropertyConfig([\n    'id' =\u003e [\n        'type' =\u003e 'int',\n        'flags' =\u003e ['primary']\n    ],\n\n    'name' =\u003e [\n        'type' =\u003e 'string'      \n    ],\n\n    //..This is the list of products contained within a category\n    'products' =\u003e [\n      'type' =\u003e 'array',\n      'flags' =\u003e ['noinsert','noupdate'],\n      'clazz' =\u003e ProductModel::class\n    ]        \n  ], \n  'id' //..Primary key property name used as the junction link target \n));\n\n\n//..Define the product model \nclass ProductModel extends \\buffalokiwi\\magicgraph\\ServiceableModel {}\n\n\n\n//..Create the product model property configuration \n$pProps =  new \\buffalokiwi\\magicgraph\\property\\QuickPropertySet( new \\buffalokiwi\\magicgraph\\junctionprovider\\QuickJunctionPropertyConfig([\n    'id' =\u003e [\n        'type' =\u003e 'int',\n        'flags' =\u003e ['primary']\n    ],\n\n    'name' =\u003e [\n        'type' =\u003e 'string'      \n    ],\n\n    //..The list of categories containing some product\n    'categories' =\u003e [\n      'type' =\u003e 'array',\n      'flags' =\u003e ['noinsert','noupdate'],\n      'clazz' =\u003e CategoryModel::class\n    ]        \n  ],\n  'id' //..Primary key property name used as the junction link target \n));\n\n\n//..Create the transaction factory\n$tFact = new \\buffalokiwi\\magicgraph\\persist\\DefaultTransactionFactory();\n    \n\n//..Create a SQL Database connection \n$dbFactory = new buffalokiwi\\magicgraph\\pdo\\PDOConnectionFactory( //..A factory for managing and sharing connection instances \n  new buffalokiwi\\magicgraph\\pdo\\MariaConnectionProperties(       //..Connection properties for MariaDB / MySQL\n    'localhost',                  //..Database server host name \n    'root',                       //..User name\n    '',                           //..Password\n    'retailrack' ),               //..Database \n  //..This is the factory method, which is used to create database connection instances\n  //..The above-defined connection arguments are passed to the closure.\n  function( buffalokiwi\\magicgraph\\pdo\\IConnectionProperties $args  ) { \n    //..Return a MariaDB connection \n    return new buffalokiwi\\magicgraph\\pdo\\MariaDBConnection( $args );\n  }\n);\n\n\n//..Create the repository for the junction table \n$jRepo = new buffalokiwi\\magicgraph\\junctionprovider\\DefaultMySQLJunctionRepo(\n  'product_category_link',\n  $dbFactory-\u003egetConnection()\n);\n  \n\n//..Create the product repository \n$pRepo = new buffalokiwi\\magicgraph\\persist\\DefaultSQLServiceableRepository(\n  'product',\n  $dbFactory-\u003egetConnection(),\n  ProductModel::class,\n  $pProps,\n  $tFact\n);\n\n\n//..Create the category repository \n$cRepo = new buffalokiwi\\magicgraph\\persist\\DefaultSQLServiceableRepository(\n  'product_category',\n  $dbFactory-\u003egetConnection(),\n  CategoryModel::class,\n  $cProps,\n  $tFact\n);\n\n\n//..Since we want both models to reference each other, we cannot instantiate the junction providers until\n//  both parent and target repositories have been created.\n//..There is a handy method for adding these: addModelPropertyProvider()\n//\n//..If we were only referencing the target models in the parent repository or vice versa, we would have passed the junction\n//..model instance directly to the serviceable repository constructor \n\n\n//..Add the junction model property provider \n$pRepo-\u003eaddModelPropertyProvider(\n  new buffalokiwi\\magicgraph\\junctionprovider\\MySQLJunctionPropertyService( \n    new buffalokiwi\\magicgraph\\junctionprovider\\JunctionModelPropSvcCfg(\n      'id',\n      'categories' ),\n    $jRepo,\n    $cRepo\n));\n\n\n$cRepo-\u003eaddModelPropertyProvider(\n  new buffalokiwi\\magicgraph\\junctionprovider\\MySQLJunctionPropertyService( \n    new buffalokiwi\\magicgraph\\junctionprovider\\JunctionModelPropSvcCfg(\n      'id',\n      'products' ),\n    $jRepo,\n    $pRepo\n));\n\n\n//..Get and print the product model \n$p1 = $pRepo-\u003eget('1');\nvar_dump( $p1-\u003etoArray(null,true,true));\n\nOutputs:\n\narray (size=3)\n  'id' =\u003e int 1\n  'name' =\u003e string 'product1' (length=8)\n  'categories' =\u003e \n    array (size=1)\n      0 =\u003e \n        array (size=3)\n          'id' =\u003e int 1\n          'name' =\u003e string 'category1' (length=9)\n          'products' =\u003e \n            array (size=1)\n              0 =\u003e \n                array (size=3)\n                  'id' =\u003e int 1\n                  'name' =\u003e string 'product1' (length=8)\n                  'categories' =\u003e \n                    array (size=1)\n                      ...\n\n\n//..Get and print the category model\n$c1 = $cRepo-\u003eget('1');\nvar_dump( $p1-\u003etoArray(null,true,true));\n\n\nOutputs:\n\narray (size=3)\n  'id' =\u003e int 1\n  'name' =\u003e string 'category1' (length=9)\n  'products' =\u003e \n    array (size=1)\n      0 =\u003e \n        array (size=3)\n          'id' =\u003e int 1\n          'name' =\u003e string 'product1' (length=8)\n          'categories' =\u003e \n            array (size=1)\n              0 =\u003e \n                array (size=3)\n                  'id' =\u003e int 1\n                  'name' =\u003e string 'category1' (length=9)\n                  'products' =\u003e \n                    array (size=1)\n                      ...\n\n\n```\n\n\n\n### Nested Relationship Providers\n\nNesting is accomplished by using the same methods outlined in the [Relationships](#relationships) chapter.  \n\nAs I'm sure you've noticed in the above many to many example, relationship providers can be used to create a series of nested\nobjects.  Relationship providers can be plugged into any property in any model, which means we can use them to create a \nsnazzy tree of objects.  Relationship providers can be used to back any model property \n\nFirst, we start by creating 3 simple tables.  For this example, the tables will only contain an id column.\n\nCreate some tables and insert a few values.  To keep this simple, we will use an id of \"1\" for everything.\n\n```sql\n\n  \ncreate table tablea( id int, primary key(id) ) engine=innodb;\ncreate table tableb( id int, primary key(id) ) engine=innodb;\ncreate table tablec( id int, primary key(id) ) engine=innodb;\n\ninsert into tablea values(1);\ninsert into tableb values(1);\ninsert into tablec values(1);\n\n```\n\n\nNext, we create a serviceable model and corresponding property set for each of the tables.  We are going to assume that \nwe have a variable $dbFactory, which is an instance of IConnectionFactory.  There is also a variable $tfact,which is an \ninstance of ITransactionFactory.  These are detailed in examples from previous chapters.\n\n\n```php\n\nclass Table1Model extends buffalokiwi\\magicgraph\\ServiceableModel {}\nclass Table2Model extends buffalokiwi\\magicgraph\\ServiceableModel {}\nclass Table3Model extends buffalokiwi\\magicgraph\\DefaultModel {}\n\n$t1Props = new buffalokiwi\\magicgraph\\property\\QuickPropertySet([\n   'id' =\u003e [\n       'type' =\u003e 'int',\n       'flags' =\u003e ['primary']\n   ],\n    \n   'table2model' =\u003e [\n       'type' =\u003e 'model',\n       'clazz' =\u003e Table2Model::class,\n       'flags' =\u003e ['noinsert','noupdate','null'], //..Table2Model requires constructor arguments. Use null here.\n   ]\n]);\n\n\n$t2Props = new buffalokiwi\\magicgraph\\property\\QuickPropertySet([\n   'id' =\u003e [\n       'type' =\u003e 'int',\n       'flags' =\u003e ['primary']\n   ],\n    \n   'table3model' =\u003e [\n       'type' =\u003e 'model',\n       'clazz' =\u003e Table3Model::class,\n       'flags' =\u003e ['noinsert','noupdate','null'], //..Table3Model requires constructor arguments. Use null here.\n   ]\n]);\n\n$t3Props = new buffalokiwi\\magicgraph\\property\\QuickPropertySet([\n   'id' =\u003e [\n       'type' =\u003e 'int',\n       'flags' =\u003e ['primary']\n   ]\n]);\n\n```\n\nAfter creating the models, we will need to create a repository for each type of model.  For this, we will use the DefaultSQLServiceableRepository, \nwhich along with ServiceableModel, allows us to use relationship providers.  Repositories controlling models located at the \nedges of the object graph will need to be created first.  ie: TableC, then TableB, then TableA.\n\n\n```php\n\n//..There are no relationships in tableC\n$t3Repo = new buffalokiwi\\magicgraph\\persist\\DefaultSQLRepository(\n  'tablec',\n  $dbFactory-\u003egetConnection(),\n  Table3Model::class,\n  $t3Props,\n);\n\n$t2Repo = new buffalokiwi\\magicgraph\\persist\\DefaultSQLServiceableRepository(\n  'tableb',\n  $dbFactory-\u003egetConnection(),\n  Table2Model::class,\n  $t2Props,\n  $tfact,\n  new buffalokiwi\\magicgraph\\OneOnePropertyService( new \\buffalokiwi\\magicgraph\\OneOnePropSvcCfg(\n    $t3Repo,\n    'id',\n    'table3model'\n)));\n\n$t1Repo = new buffalokiwi\\magicgraph\\persist\\DefaultSQLServiceableRepository(\n  'tablea',\n  $dbFactory-\u003egetConnection(),\n  Table1Model::class,\n  $t1Props,\n  $tfact,\n  new buffalokiwi\\magicgraph\\OneOnePropertyService( new \\buffalokiwi\\magicgraph\\OneOnePropSvcCfg(\n    $t2Repo,\n    'id',\n    'table2model'\n)));\n\n```\n\nFinally, we get the model from tablea, and we print the graph:\n\n```php\n\n$model1 = $t1Repo-\u003eget(\"1\");\n\nvar_dump( $model1-\u003etoArray( null, true, true ));\n\nOutputs:\n\narray (size=2)\n  'id' =\u003e int 1\n  'table2model' =\u003e \n    array (size=2)\n      'id' =\u003e int 1\n      'table3model' =\u003e \n        array (size=1)\n          'id' =\u003e int 1\n\n```\n\nAny relationship provider will work in exactly the same way as the one to one provider.\n\n\n\n### How Editing and Saving Works\n\n\nAs detailed in the [Saveable Mapping Object Factory](#saveable-mapping-object-factory) section, models can be saved \nsomewhere by calling the [ISaveableObjectFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ISaveableObjectFactory.html) \nsave method.  This section will deal with how editing and saving works when using relationship providers.\n\nEditing and saving is controlled both by the relationship provider and the edited property tracking system built into \n[ServiceableModel](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-DefaultModel.html#method_hasEdits).  \nWhen a serviceable model is saved by some serviceable repository, calling the save method causes the serviceable repository to \nfetch save functions from the relationship providers.  \n\nRelationship providers must implement the [IModelPropertyProvider](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-IModelPropertyProvider.html)\ninterface.  The method IModelPropertyProvider::getSaveFunction will return a function which is passed to a [ITransactionFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ITransactionFactory.html) \nwhere the save function is called, and the related model is persisted.\n\nThe [One to One](#one-to-one) relationship provider is relatively straightforward.  Any model properties backed by a\n[OneOnePropertyService](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-OneOnePropertyService.html) \nwill be automatically saved when the parent model is saved via some repository.  This will work with new models and also existing \nmodels loaded by the provider.\n\nThe [One to Many](#one-to-many) and [Many to Many](#many-to-many) relationship providers will manage array properties \ncontaining IModel instances.  This is slightly more complicated than the one to one provider.  In addition to saving \nedited models, the one to many and many to many providers will also manage inserts and deletes.  If new models are added to the \narray property, they will be inserted into the database.  If an existing model is removed from the array property, it \nwill be deleted from the database.  If any filters or limits are used when loading related models, the delete functionality \nis disabled, and models must be manually unlinked via the repository controlling that model type.\n\nSaves will automatically cascade when using nested relationship providers.  Any nested model at any position in the object \ngraph can be edited, and when the top-most model is saved, it will \n\n\n---\n\n\n## Extensible Models\n\nWe're finally through the foundational concepts, woohoo!  \n\nMagic Graph models are designed to be as flexible as possible.  As I'm sure you've noticed, there are several ways to \nconfigure models, and each of those ways have different levels of extensibility.  For example, models can be created by \nusing simple property annotations, or they can be created at runtime by using property configuration objects.  While using\nthe annotated properties is quick and easy, it is nowhere near as scalable as using property configuration objects.  \n\n\nThrough the use of property configuration objects we can:\n\n1. Define the properties that will existing within a given model\n2. Provide run time type information for properties.  For example, config objects can implement methods to return property names, which \ncan be used to query property meta data in a model's property set.\n3. Add additional meta data to properties \n4. Provide the ability to swap out the list of properties used for persistence.  For example, if we want to share a model \nbetween multiple persistence types that have different property names, we can swap out the configuration object.\n5. Attach simple behaviors to individual properties.  ie: get, set, change, etc.\n6. Extend save functionality through functions like: before save, after save, on save, save function, etc.\n7. Property configuration can be dynamically generated at runtime, which allows us to implement patterns such as EAV.\n8. Multiple property configuration objects can be used to create a single model.  This allows developers to create \nmodel extensions or to separate concerns into different packages.\n\n\n### Property Configuration Interface\n\nFor configuration array definitions, please see [Property Configuration](#property-configuration).  \n\nProperty configuration objects must all implement the [IPropertyConfig](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyConfig.html) \ninterface.  This interface is used by implementations of [IPropertySetFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertySetFactory.html) \nto create [IPropertySet](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertySet.html) instances, \nwhich contain all of the relevant properties, meta data, and behaviors used by [IModel](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-IModel.html) instances.\n\nThe IPropertyConfig interface currently contains four methods:\n\n\n```php\ngetConfig(): array\n```\ngetConfig() is called by IPropertySetFactory, and returns the property configuration array.  This array contains \nall of the property definitions, meta data, and optional event handlers.\n\n\n```php\ngetPropertyNames(): array\n```\ngetPropertyNames() will return a list of strings containing each property name defined by this configuration object.\n\n\n```php\nbeforeSave( IModel $model ) : void;\n```\nbeforeSave() is called by an IRepository prior to a model being persisted.  This is an opportunity to modify the model's \nstate or add additional validation prior to save.  When creating the beforeSave() handler, the IPropertyConfig implementation\nSHOULD iterate over the properties defined in the configuration array, and call each property-level beforeSave handler.\n\n\n```php\nafterSave( IModel $model ) : void;\n```\nafterSave() is called by an IRepository after a model has been persisted, but before commit().  This can be used to clean \nup after a save, check the results of a save, etc.  When creating the afterSave() handler, the IPropertyConfig implementation\nSHOULD iterate over the properties defined in the configuration array, and call each property-level afterSave handler.\n\n\n\n### Property Configuration Implementation\n\nNow that we know how a property configuration object, and the configuration array is defined, let's build out a complete\nimplementation.  Magic Graph ships with a abstract base class \n[BasePropertyConfig](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-BasePropertyConfig.html), \nwhich contains constants for commonly used property configurations and adds the ability to incorporate behavioral strategies by \npassing [INamedPropertyBehavior](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-INamedPropertyBehavior.html) instances to the constructor.\n\n\nLet's make a basic rectangle model.  In this example, we will create two classes: Rectangle and RectangleProperties.  \nRectangle is the value object, and RectangleProperties defines the properties contained within the Rectangle value object.\n\n\n```php\n/**\n * Property configuration for a rectangle value object\n */\nclass RectangleProperties extends buffalokiwi\\magicgraph\\property\\BasePropertyConfig\n{\n  /**\n   * Height property name \n   */\n  const HEIGHT = 'height';\n  \n  /**\n   * Width property name \n   */\n  const WIDTH = 'width';\n  \n  \n  /**\n   * Returns the property configuration array \n   * @return array \n   */\n  protected function createConfig() : array\n  {\n    return [\n      self::HEIGHT =\u003e self::FINTEGER_REQUIRED,\n      self::WIDTH =\u003e self::FINTEGER_REQUIRED\n    ];\n  }\n}\n\n\n/**\n * Rectangle Value Object\n */\nclass Rectangle extends buffalokiwi\\magicgraph\\GenericModel {}\n\n//..Create the rectangle model instance \n$rectangle = new Rectangle( new RectangleProperties());\n\n\n/**\n * Outputs:\n * array (size=2)\n *   'height' =\u003e int 0\n *   'width' =\u003e int 0\n */\nvar_dump( $rectangle-\u003etoArray());\n\n/**\n * Throws Exception with message: \n * \"height\" property of class \"Rectangle\" of type \"int\" is REQUIRED and must not be empty.\n */\n$rectangle-\u003evalidate();\n\n```\n\nThe above example is fairly straightforward.  A configuration object defines two required properties, height and width.\nWhen the model is instantiated, height and width are both zero.  This is because the default value each property is zero, and \ndefault values will bypass property validation.  When IModel::validate() is called, both properties are validated and will \nthrow a [ValidationException](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-ValidationException.html).\n\nThat's great and all, but properties should validate when they are set, right?  The required property flag will only validate\nwhen IModel::validate() is called, so if we want to validate when the property is set, we must add a validation callback.\n\nWe can rewrite the createConfig function like this:\n\n```php\nprotected function createConfig() : array\n{\n  //..Validation callback that will throw an exception when setting an integer property value to zero\n  $vInt = fn( buffalokiwi\\magicgraph\\property\\IProperty $prop, int $value ) : bool =\u003e !empty( $value );\n\n  return [\n    self::HEIGHT =\u003e self::FINTEGER_REQUIRED + [self::VALIDATE =\u003e $vInt],\n    self::WIDTH =\u003e self::FINTEGER_REQUIRED + [self::VALIDATE =\u003e $vInt]\n  ];\n}\n```\n\nAfter the model as been created using the above change, setting height or width equal to zero will throw an exception.\nThis does not affect the default property value of zero.  Default values will bypass validation when a property instance is created.\n\n```php\n//..Set height to zero\n$rectangle-\u003eheight = 0;\n\n//..Throws an exception like:\n//  Behavior validation failure in closure: RectangleProperties in file test.php on line 71\n```\n\n\nWhat if we wanted to make a rectangle behave as a square?  Since square is a specialization of a rectangle, we can \ncreate a behavioral strategy which will be used to enforce the height must equal width rule.  When height is set, width \nwill automatically be set to the value of height and vise versa.  \n\n\nFirst we will want to create an interface for RectangleProperties.  This will define two methods getHeight() and getWidth(), which \nwill return the model property names for height and width.  This interface is how we will ensure that only rectangles \nare used with the behavioral strategy, and it's also a great way to decouple property names from the database column names.\n\n\n```php\n/**\n * This interface defines a property configuration object for a Rectangle.\n */\ninterface IRectangleProperties extends \\buffalokiwi\\magicgraph\\property\\IPropertyConfig\n{\n  /**\n   * Get the height property name \n   * @return string\n   */\n  public function getHeight() : string;\n  \n  \n  /**\n   * Get the width property name \n   * @return string\n   */\n  public function getWidth() : string;\n}\n```\n\n\nAnd our modified RectangleProperties class now looks like this:\n\n```php\n/**\n * Property configuration for a rectangle value object\n */\nclass RectangleProperties extends buffalokiwi\\magicgraph\\property\\BasePropertyConfig implements IRectangleProperties\n{\n  /**\n   * Height property name in the database\n   */\n  const HEIGHT = 'height';\n  \n  /**\n   * Width property name in the database \n   */\n  const WIDTH = 'width';\n  \n  \n  /**\n   * Get the height property name \n   * @return string\n   */\n  public function getHeight() : string\n  {\n    return self::HEIGHT;\n  }\n  \n  \n  /**\n   * Get the width property name \n   * @return string\n   */\n  public function getWidth() : string\n  {\n    return self::WIDTH;\n  }\n  \n  \n  /**\n   * Returns the property configuration array \n   * @return array \n   */\n  protected function createConfig() : array\n  {\n    //..Zero is no longer allowed\n    $vInt = fn( buffalokiwi\\magicgraph\\property\\IProperty $prop, int $value ) : bool =\u003e !empty( $value );\n    \n    return [\n      self::HEIGHT =\u003e self::FINTEGER_REQUIRED + [self::VALIDATE =\u003e $vInt],\n      self::WIDTH =\u003e self::FINTEGER_REQUIRED + [self::VALIDATE =\u003e $vInt]\n    ];\n  }\n}\n```\n\n\nNow that the property configuration is properly configured, we can create a behavioral strategy that will make \nheight always equal to width in any model that uses IRectangleProperties.  To accomplish this, we will create a class\ncalled BehaveAsSquare, which descends from GenericNamedPropertyBehavior.  GenericNamedPropertyBehavior is normally \nused to attach behavior to a single property.  \n\nFor our square behavior, we want to use the model setter callback \n(called any time a property value is set by IModel::setValue()).  This means we need to pass static::class to \nthe GenericNamedPropertyBehavior constructor.  When the property name is equal to the class name, the behavior will \nbe applied to every property in a model.  This will allow us to write a single handler for multiple properties.\n\n\n```php\n/**\n * Causes rectangles to behave as squares.\n * This uses the model setter callback to force height and width to always be equal.\n */\nclass BehaveAsSquare extends buffalokiwi\\magicgraph\\property\\GenericNamedPropertyBehavior\n{\n  /**\n   * Model setter callback \n   * @var \\Closure\n   */\n  private \\Closure $mSetter;\n  \n  \n  public function __construct()\n  {\n    parent::__construct( static::class );\n    $this-\u003emSetter = $this-\u003ecreateModelSetterCallback();\n  }\n  \n  \n  /**\n   * Return the model setter callback\n   * @return \\Closure|null\n   */\n  public function getModelSetterCallback(): ?\\Closure\n  {\n    return $this-\u003emSetter;\n  }\n  \n  \n  /**\n   * Creates the model setter callback.  \n   * No need to create this every time the setter is called.\n   * @return \\Closure\n   */\n  private function createModelSetterCallback() : \\Closure \n  {\n    //..This setter is a circular reference, so we want to know if we're already in the closure\n    $inClosure = false;\n    \n    return function( \n      \\buffalokiwi\\magicgraph\\IModel $model, \n      \\buffalokiwi\\magicgraph\\property\\IProperty $prop, \n      $value ) use(\u0026$inClosure) : mixed \n    { \n      //..Return if already in closure \n      if ( $inClosure )\n        return $value;      \n      \n      //..Set the state\n      $inClosure = true;\n      \n      //..Get the rectangle property config \n      //..This will throw an exception if rectangleproperties are not used in the model.\n      /* @var $props IRectangleProperties */\n      $props = $model-\u003egetPropertyConfig( IRectangleProperties::class );\n      \n      //..Set the other dimension \n      switch( $prop-\u003egetName())\n      {\n        case $props-\u003egetHeight():\n          $model-\u003esetValue( $props-\u003egetWidth(), $value );\n        break;\n\n        case $props-\u003egetWidth():\n          $model-\u003esetValue( $props-\u003egetHeight(), $value );\n        break;\n      }\n\n      try {\n        return $value;\n      } finally {\n        //..Reset state\n        $inClosure = false;\n      }\n    };    \n  }\n}\n```\n\nTo make our rectangle behave as a square, we can initialize it like this:\n\n```php\n//..Create the rectangle model instance and make it a square \n$rectangle = new Rectangle( new RectangleProperties( new BehaveAsSquare()));\n\n//..Set one dimension\n$rectangle-\u003eheight = 10;\n\n/**\n * Outputs:\n * array (size=2)\n *   'height' =\u003e int 10\n *   'width' =\u003e int 10\n */\nvar_dump( $rectangle-\u003etoArray());\n```\n\nSince we all know a rectangle and a square aren't the same thing, we can use the same property configuration, and our new behavioral \nstrategy to create two new models: Rectangle and Square.\n\n```php\n\n/**\n * Rectangle Value Object\n */\nclass Rectangle extends buffalokiwi\\magicgraph\\GenericModel \n{\n  private IRectangleProperties $props;\n         \n  public function __construct( \\buffalokiwi\\magicgraph\\property\\IPropertyConfig ...$config )\n  {\n    parent::__construct( ...$config );\n    //..Here we ensure that the model is actually a rectangle, and we get the property names.\n    $this-\u003eprops = $this-\u003egetPropertyConfig( IRectangleProperties::class );\n  }\n  \n\n  /**\n   * Sets the rectangle dimensions \n   * @param int $height Height \n   * @param int $width Width \n   * @return void\n   */      \n  public function setDimensions( int $height, int $width ) : void\n  {\n    $this-\u003esetValue( $this-\u003eprops-\u003egetHeight(), $height );\n    $this-\u003esetValue( $this-\u003eprops-\u003egetWidth(), $width );\n  }\n  \n  \n  /**\n   * Gets the height \n   * @return int\n   */\n  public function getHeight() : int\n  {\n    return $this-\u003egetValue( $this-\u003eprops-\u003egetHeight());\n  }\n  \n  \n  /**\n   * Gets the width \n   * @return int\n   */\n  public function getWidth() : int\n  {\n    return $this-\u003egetValue( $this-\u003eprops-\u003egetWidth());\n  }\n}\n\n\n/**\n * Square value object\n * Height and width are always equal \n */\nclass Square extends buffalokiwi\\magicgraph\\GenericModel \n{\n  private IRectangleProperties $props;\n         \n  public function __construct( \\buffalokiwi\\magicgraph\\property\\IPropertyConfig ...$config )\n  {\n    parent::__construct( ...$config );\n    //..Here we ensure that the model is actually a rectangle, and we get the property names.\n    $this-\u003eprops = $this-\u003egetPropertyConfig( IRectangleProperties::class );\n  }\n  \n\n  /**\n   * Sets the rectangle dimensions \n   * @param int $height Height \n   * @param int $width Width \n   * @return void\n   */      \n  public function setDimension( int $heightAndWidth ) : void\n  {\n    //..Our BehaveAsSquare will handle this \n    //..We could have just as easily set both properties here, but this is an example of how strategies work.\n    $this-\u003esetValue( $this-\u003eprops-\u003egetHeight(), $heightAndWidth );\n  }\n  \n  \n  /**\n   * Gets the height \n   * @return int\n   */\n  public function getHeight() : int\n  {\n    return $this-\u003egetValue( $this-\u003eprops-\u003egetHeight());\n  }  \n}\n```\n\nAnd finally, we can create an instance of a square:\n\n```php\n$square = new Square( new RectangleProperties( new BehaveAsSquare()));\n\n$square-\u003esetDimension( 10 );\n\n/**\n * Outputs:\n * array (size=2)\n *   'height' =\u003e int 10\n *   'width' =\u003e int 10\n */\nvar_dump( $square-\u003etoArray());\n```\n\n\n### Using multiple property configurations\n\n\nIt's possible to use multiple IPropertyConfig objects to create a single model.  This is one of the more useful features\nof property configuration objects. It's possible for one package to define a model, and have other packages extend that \nmodel by adding properties and behavior.  Each property configuration can also incorporate any inline event handlers and\nzero or more behavioral strategies.  Think of this as a sort of plugin system.  Here's an example:\n\nFirst we create two property configurations:\n\n```php\nclass FooProps extends buffalokiwi\\magicgraph\\property\\BasePropertyConfig\n{\n  protected function createConfig(): array\n  {\n    return [\n      'foo' =\u003e self::FSTRING\n    ];\n  }\n}\n\n\nclass BarProps extends buffalokiwi\\magicgraph\\property\\BasePropertyConfig\n{\n  protected function createConfig(): array\n  {\n    return [\n      'bar' =\u003e self::FSTRING\n    ];\n  }  \n}\n```\n\nNow we can pass an instance of each configuration object to a model (or property set) constructor.\n\n```php\n//..Create the model instance with both property configuration objects \n$model = new buffalokiwi\\magicgraph\\GenericModel( new FooProps(), new BarProps());\n\n\n/**\n * Outputs:\n * array (size=2)\n *   'foo' =\u003e string '' (length=0)\n *   'bar' =\u003e string '' (length=0)\n */\nvar_dump( $model-\u003etoArray());\n```\n\nProperties from both configurations will appear in the model.\n\nWe can also add properties at runtime.  Here's a third configuration we'll add to the model.\n\n```php\nclass BazProps extends buffalokiwi\\magicgraph\\property\\BasePropertyConfig\n{\n  protected function createConfig(): array\n  {\n    return [\n      'baz' =\u003e self::FSTRING\n    ];\n  }  \n}\n```\n\nAdding a configuration object at runtime is done through the property set:\n\n```php\n$model-\u003egetPropertySet()-\u003eaddPropertyConfig( new BazProps());\n\n/**\n * Outputs:\n * array (size=2)\n *   'foo' =\u003e string '' (length=0)\n *   'bar' =\u003e string '' (length=0)\n *   'baz' =\u003e string '' (length=0)\n */\nvar_dump( $model-\u003etoArray());\n```\n\n\n---\n\n\n### Model Interface \n\nAll models in Magic Graph must implement the [IModel](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-IModel.html) interface. \nThe interface itself is fairly simple and straightforward.  \n\nIModel focuses on a few key areas, Properties, Validation, State, Serialization and Cloning:\n\n1. **Properties**\n    1. **instanceOf()** - Tests that a IPropertyConfig instance is a or implements the supplied class or interface name.  This is used to test if a model is \"of some type\".\n    2. **getPropertySet()** - Retrieve the internal IPropertySet instance containing the properties used in the model\n    3. **getPropertyNameSet()** - Retrieve an instance of [IBigSet](https://github.com/SixArmDonkey/buffalotools_types#bigset) containing a list of property names in the property set.  This is used for \nmethods that utilize model property names.  ie: toArray() can return a limited list of properties by supplying an instance of \nIBigSet containing active bits for each of the desired properties.\n    4. **getPropertyNameSetByFlags()** - The same as getPropertyNameSet() and includes the ability to filter by enabled property flags.\n    5. **getPropertyConfig()** - Retrieve an array containing the property configuration used to create the model properties\n    6. **getIterator()** - Retrieve an iterator used to iterate over any non-array and non-model properties and values contained in the model.\n2. **Validation**\n    1. **validate()** - Individually validates each property, and the first property to test as invalid will throw a ValidationException\n    2. **validateAll()** - Validates each property in the model and stores the results in a list.  Properties with failed validation are returned as a map.\n3. **State**\n    1. **getModifiedProperties()** - Retrieve an instance of IBigSet with the bits for any edited properties enabled \n    2. **getInsertProperties()** - Retrieve an instance of IBigSet with the bits for any properties required for a database \"insert\".\n    3. **hasEdits()** - Tests if any properties have been edited since initialization \n4. **Serialization**\n    1. **toArray()** - Used for persistence, debugging and other fun things.  Converts the IModel instance into a multi-dimensional array.\n    2. **toObject()** - Used for JSON Serialization, converts IModel to an object graph.\n    3. **fromArray()** - Used to initialize the model with data from the persistence layer.  Populates any matching IModel properties with the supplied values.\n    4. **jsonSerialize()** - Usually calls toObject().\n5. **Cloning**\n    1. **__clone()** - IModel instances are cloneable.\n    2. **createCopy()** - Preferred over __clone, this can be used to clone or copy (without primary keys) models and also cause them to be read only.\n\n\n### Model Implementation \n\nModels are composed of a few components, a property set containing all various object property instances and the model\nimplementation.  Currently, every property set extends [DefaultPropertySet](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-DefaultPropertySet.html) \nand every model extends [DefaultModel](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-DefaultModel.html).\n\nIt is worth noting that DefaultModel contains quite a bit of functionality.  Instead of directly implementing IModel, it \nis recommended to extend all models from DefaultModel.  \n\nAt the time of writing, Magic Graph ships with 7 IModel implementations and two decorators:\n\n1. **DefaultModel** - The base model.  Every model should extend from this class.  \n    1. **ServiceableModel** - Extends DefaultModel and adds the necessary functionality required to support relationship providers.\n    2. **AnnotatedModel** - Can use attributes in php8 to configure and initialize model properties.  \n    3. **GenericModel** - Quick way to create a model using IPropertyConfig.\n    4. **QuickModel** - Quick way to create a model with nothing other than the property configuration array.\n    5. **QuickServiceableModel** \n    6. **ProxyModel** - Used to decorate IModel instances \n        1. **ReadOnlyModelWrapper** - A decorator for IModel that disables setting property values\n        2. **ServiceableModelWrapper** - A decorator for IModel that can add relationship providers to model instances.\n\nThe quick and generic model variants are easier to instantiate, but using these models prevents you from selecting the \nproperty set, config mapper and property factory.  Internally, quick and generic models all use instances of DefaultPropertySet, DefaultConfigMapper and PropertyFactory.\n\n\n---\n\n\n## Behavioral Strategies\n\nThis has already been detailed in the [Property Behavior](#property-behavior) section, but since this might be the \nmost important topic in all of Magic Graph, we're going to go over it again.\n\nThe goals of behavioral strategies are the following:\n\n1. Reduce the complexity of models\n2. Increase the ease of writing tests\n3. Introduce or replace functionality without extending or modifying the model \n\n\nWe've all seen models that try to do it all.  The messy code, the stinky code.  Things like support for third party packages \nhacked into models, ignoring separation of concerns, or referencing objects the model should know nothing about.  There are many\nsolutions to these problems, but most of the time I see developers write a service used to join several packages together.  This is great and all, \nbut it still tightly couples packages and adds complexity.  If repositories are in use, and there's a separate service on top of \nsaid repository, which one should the developer use to save the model?  What happens if some code is written that doesn't know \nabout the service?  Shenanigans ensue.\n\nBehavioral Strategies are an attempt to simplify inter-package relationships.  Think of a strategy like an adapter.  We \ncan write a program with tests, then attach the program to a model.  The model and/or repository will then dispatch events, \nwhich the strategy program will use to either change the model's state and/or introduce side effects.  \n\nIn this context, side effects may not be a bad thing.  For example, say we have an ecommerce platform, and we want to generate a shipping\nlabel when an order has been packaged and is ready to ship.  We could write a strategy that monitors an order's status, knows how to interact with some \nshipping api, and generates a shipping label when the order's state moves to \"ready to ship\".  This strategy is simply attached\nto the repository and model during object creation in composition root.  We now have an independently-testable program, which \nadds support for shipping api's to the order model without needing to modify the order model, repository or create a service layer.\n\nBehavioral strategy programs are basically event handlers for various events fired by IProperty, IModel and IRepository.  Currently, there\nare a few behavior interfaces.\n\n[IPropertyBehavior](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyBehavior.html) is primarily used by the property configuration array, and contains several callbacks related to a single property.\nAll callbacks will include an argument IProperty, which is the property that triggered the callback.  \n\n\n**Validation Callback**\n\nThe validation callback is called any time IProperty::validate() is invoked.  \n\n```php\nfunction getValidationCallback() : ?Closure\n{\n  /**\n   * Validate some property value \n   * @param buffalokiwi\\magicgraph\\property\\IProperty $prop Property being validated\n   * @param mixed $value Value to validate\n   * @return bool is valid\n   */\n  return function( IProperty $prop, mixed $value ) : bool {\n    //..Validate $value \n    return false; //..Not valid, throws an exception \n  };\n}\n```\n\n\n**Setter Callback**\n\nThe setter callback is called before IProperty::validate().  The purpose of this callback is to modify the value prior to \nit being written to the backing property object.  Think of this as serializing a property value.\n```php\nfunction getSetterCallback() : ?Closure\n{\n  /**\n   * Modify a property value prior to being written to the backing property\n   * @param buffalokiwi\\magicgraph\\property\\IProperty $prop Property being set \n   * @param mixed $value Value to set \n   * @return mixed modified value \n   */  \n  return function( buffalokiwi\\magicgraph\\property\\IProperty $prop, mixed $value ) : mixed {\n    //..Ensure that any incoming value is a string, then append 'bar'\n    return (string)$value . 'bar';\n  };\n}\n```\n\n\n**Getter Callback**\n\nThe getter callback is called prior to returning a value from IProperty::getValue().  This is to modify the value \nstored in the backing property object prior to using it.  Think of this as deserializing a property value.\nNotice the $context argument on the getter callback.  IModel::setValue() contains a context argument, and this can be used\nto set some arbitrary context/meta data/state/etc used in the getter callbacks.  \n```php\nfunction getGetterCallback() : ?Closure\n{\n  /**\n   * Modify a property value prior to being written to the backing property\n   * @param buffalokiwi\\magicgraph\\property\\IProperty $prop Property being set \n   * @param mixed $value Value to set \n   * @param array $context The context \n   * @return mixed modified value \n   */  \n  return function( buffalokiwi\\magicgraph\\property\\IProperty $prop, mixed $value, array $context ) : mixed {\n    //..Ensure that any incoming value is a string, then append 'bar'\n    return (string)$value . 'bar';\n  };\n}\n```\n\n\n**Init Callback**\n\nThe init callback is used to modify the default value prior to it being written to the backing object.  This is called\nwhen IProperty::reset() is called.  This is never run through IProperty::validate(), so be careful with default values.\n```php\nfunction getInitCallback() : ?Closure\n{\n  /**\n   * Modify the default value \n   * @param mixed $value The default value \n   * @return mixed default value \n   */\n  return function ( mixed $value ) : mixed {\n    return $value;\n  };\n}\n```\n\n\n**Empty Callback**\n\nThe empty callback is useful in situations where empty() does not return true, but whatever the value is should still be considered empty.\nFor example, if the property is a object representing a primitive, then empty() would return false even if the objects internal value \nwas actually empty.  \n```php\nfunction getIsEmptyCallback() : ?Closure\n{\n  /**\n   * Basic empty check that returns true if the value is empty or the value is equal to the default property value.\n   * @param buffalokiwi\\magicgraph\\property\\IProperty $prop Property being tested\n   * @param mixed $value The value to test\n   * @param mixed $defaultValue The default value for the property. \n   * @return bool is empty \n   */\n  return function ( buffalokiwi\\magicgraph\\property\\IProperty $prop, mixed $value, mixed $defaultValue ) : bool {\n    return empty( $value ) || $value === $defaultValue;\n  };\n}\n```\n\n\n**Change Callback**\n\nWhen a property value changes, this callback is fired.  It is worth noting, that this happens at the property level, \nnot inside of any models.  Therefore, this event will have no access to other properties in the model.  Due to this restriction, \nthere may be limited uses for this callback.  If you need access to other properties in a model, use the model level getter/setter callbacks.\n```php\nfunction getOnChangeCallback() : ?Closure\n{\n  /**\n   * @param buffalokiwi\\magicgraph\\property\\IProperty $prop The property being changed\n   * @param mixed $oldValue The value prior to the change\n   * @param mixed $newValue The value after the change\n   */\n  return function ( buffalokiwi\\magicgraph\\property\\IProperty $prop, mixed $oldValue, mixed $newValue ) : void {\n    //..Do something interesting \n  };\n}\n```\n\n\n### HTML Property Package Callbacks\n\n\n**HTML Input Callback**\n\nAs part of a fun little bonus to Magic Graph, all IProperty instances can be converted into their HTML counterparts.  When \nusing [IElementFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-htmlproperty-IElementFactory.html) to generate HTML inputs, this callback will be used to override the default html generated by \nthe element factory.  We will go over this further in the [Creating HTML elements](#creating-html-elements] chhapter.\n```php\nfunction getHTMLInputCallback() : ?Closure\n{\n  /**\n   * Convert IProperty to IElement for HTML output\n   * @param \\buffalokiwi\\magicgraph\\IModel $model Model property belongs to\n   * @param buffalokiwi\\magicgraph\\property\\IProperty $prop Property to convert\n   * @param string $name HTML element name attribute value\n   * @param string $id HTML element id attribute value \n   * @param mixed $value Property value\n   * @return \\buffalokiwi\\magicgraph\\property\\htmlproperty\\IElement The HTML element \n   */\n  return function (\n    \\buffalokiwi\\magicgraph\\IModel $model,\n    \\buffalokiwi\\magicgraph\\property\\IProperty $prop,\n    string $name,\n    string $id,\n    mixed $value ) : \\buffalokiwi\\magicgraph\\property\\htmlproperty\\IElement {\n    return new buffalokiwi\\magicgraph\\property\\htmlproperty\\TextAreaElement( $name, $id, $value );\n  };\n}\n```\n\n\n### Model-level callbacks\n\n\nThe following callbacks are invoked by IModel implementations.\n\n**To Array Callback**\n\nIModel::toArray() is used for persistence and serialization.  The toArray callback is invoted when converting property values to their persisted state.\n```php\nfunction getToArrayCallback() : ?Closure\n{\n  /**\n   * @param \\buffalokiwi\\magicgraph\\IModel $model Model being converted to an array\n   * @param buffalokiwi\\magicgraph\\property\\IProperty $prop Property the value belongs to\n   * @param mixed $value Value to modify \n   * @return mixed modified value \n   */\n  return function( \n    \\buffalokiwi\\magicgraph\\IModel $model, \n    buffalokiwi\\magicgraph\\property\\IProperty $prop, \n    mixed $value ) : mixed {\n    //..Return the modified value \n    return $value;\n  };\n}\n```\n\n\n**Model Setter Callback**\n\nThe model setter callback is the same as the property setter callback, except it adds access to the model and it is invoked by DefaultModel instead of AbstractProperty.\n```php\nfunction getModelSetterCallback() : ?Closure\n{\n  /**\n   * @param \\buffalokiwi\\magicgraph\\IModel $model The model the property belongs to\n   * @param buffalokiwi\\magicgraph\\property\\IProperty $prop The property being set \n   * @param mixed $value The value being written\n   * @return mixed The modified value to write to the backing property\n   */\n  return function( \n    \\buffalokiwi\\magicgraph\\IModel $model, \n    \\buffalokiwi\\magicgraph\\property\\IProperty $prop, \n    mixed $value ) : mixed {\n    //..Return modified value \n    return $value;\n  };\n}\n```\n\n\n**Model Getter Callback**\n\nThe model getter callback is the same as the property getter callback, except it adds access to the model and it is invoked by DefaultModel instead of AbstractProperty.\n```php\nfunction getModelGetterCallback() : ?Closure\n{\n  /**\n   * @param \\buffalokiwi\\magicgraph\\IModel $model The model the property belongs to\n   * @param buffalokiwi\\magicgraph\\property\\IProperty $prop The property being retrieved\n   * @param mixed $value The value being retrieved\n   * @return mixed The modified value to retrieve\n   */\n  return function( \n    \\buffalokiwi\\magicgraph\\IModel $model, \n    \\buffalokiwi\\magicgraph\\property\\IProperty $prop, \n    mixed $value ) : mixed {\n    //..Return modified value \n    return $value;\n  };\n}\n```\n\n\n### Named Property Behavior \n\nThe [INamedPropertyBehavior](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-INamedPropertyBehavior.html) interface \nextends [IPropertyBehavior](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-IPropertyBehavior.html), adds additional model-level callbacks.\n\nThe following callbacks are invoked by [ISaveableMappingObjectFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ISaveableObjectFactory.html) implementations.\n\n**Model Validation Callback**\n\nThis is called when IModel::validate() is invoked, and is an opportunity to validate the state of a model.  Any validation \nerrors must throw a [ValidationException](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-ValidationException.html) \n```php\nfunction getModelValidationCallback() : ?Closure \n{\n  /**\n   * @param \\buffalokiwi\\magicgraph\\IModel $model The model to validate \n   */\n  return function( \\buffalokiwi\\magicgraph\\IModel $model ) : void {\n    if ( !$valid )\n      throw new \\buffalokiwi\\magicgraph\\ValidationException( 'Model is invalid' );\n  };\n}\n```\n\n\n**Before Save Callback**\n\nThis is what it sounds like.  When a model is saved by some [ISaveableMappingObjectFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-persist-ISaveableObjectFactory.html) implementation,\nbefore save is called prior to the model being persisted.  \nNote: In the default repository implementations, save is part of a transaction and any exceptions thrown will trigger a rollback.\n```php\nfunction getBeforeSaveCallback() : ?Closure\n{\n  /**\n   * @param \\buffalokiwi\\magicgraph\\IModel $model The model to save\n   */\n  return function( \\buffalokiwi\\magicgraph\\IModel $model ) : void {\n    //..Do something with the model before it's saved\n  };\n}\n```\n\n\n**After Save Callback**\n\nThis is the same thing as before save, but it happens after the model is saved.\n```php\nfunction getAfterSaveCallback() : ?Closure\n{\n  /**\n   * @param \\buffalokiwi\\magicgraph\\IModel $model The model to save\n   */\n  return function( \\buffalokiwi\\magicgraph\\IModel $model ) : void {\n    //..Do something with the model after it's saved\n  };\n}\n```\n\nThere are a few ways of implementing INamedPropertyBehavior:\n\n1. Extend [buffalokiwi\\magicgraph\\property\\GenericNamedPropertyBehavior](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-GenericNamedPropertyBehavior.html)\n2. Create an anonymous strategy with the [NamedPropertyBehaviorBuilder](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-NamedPropertyBehaviorBuilder.html)\n3. Write your own implementation \n\n\nExtending GenericNamedPropertyBehavior is the preferred method of creating behavioral strategies.  By default, every callback \nwill return null.  Override any of the methods in some subclass and return the callback closures.  In the following example, we will \ncreate a model called Test with a property name.  We'll create a strategy that will set name equal to \"bar\" if name is set to \"foo\", and if \nname is set to \"baz\" and exception is thrown.\n\n```php\n/**\n * Property definition for TestModel \n */\nclass TestProperties extends buffalokiwi\\magicgraph\\property\\BasePropertyConfig\n{\n  const NAME = 'name';\n  \n  public function getName() : string\n  {\n    return self::NAME;\n  }\n  \n  protected function createConfig() : array\n  {\n    return [\n      self::NAME =\u003e self::FSTRING\n    ];\n  }\n}\n\n\n/**\n * Test model\n */\nclass TestModel extends \\buffalokiwi\\magicgraph\\GenericModel \n{\n  /**\n   * Property definitions \n   * @var TestProperties\n   */\n  private TestProperties $props;\n  \n  \n  public function __construct( \\buffalokiwi\\magicgraph\\property\\IPropertyConfig ...$config )\n  {\n    parent::__construct( ...$config );\n    $this-\u003eprops = $this-\u003egetPropertyConfig( TestProperties::class );\n  }\n  \n    \n  public function getName() : string\n  {\n    return $this-\u003egetValue( $this-\u003eprops-\u003egetName());\n  }\n  \n  \n  public function setName( string $name ) : void\n  {\n    $this-\u003esetValue( $this-\u003eprops-\u003egetName(), $name );\n  }\n}\n\n\n/**\n * If the name property equals \"foo\", it is set to \"bar\".\n * If theh name property equals \"baz\", a ValidationException is thrown \n */\nclass TestModelBehavior extends buffalokiwi\\magicgraph\\property\\GenericNamedPropertyBehavior\n{\n  public function getValidateCallback(): ?\\Closure\n  {\n    return function( buffalokiwi\\magicgraph\\property\\IProperty $prop, string $name ) : bool {\n      //..If $name equals baz, then an exception is thrown\n      return $name != 'baz';        \n    };\n  }\n  \n  \n  public function getSetterCallback(): ?\\Closure\n  {\n    return function( buffalokiwi\\magicgraph\\property\\IProperty $prop, string $name ) : string {\n      //..Returns bar if name equals foo.\n      return ( $name == 'foo' ) ? 'bar' : $name;\n    };\n  }\n}\n\n\n\n//..Create an instance of test model with the test behavior. \n//..The behavior is wired to the name property.\n$model = new TestModel( new TestProperties( new TestModelBehavior( TestProperties::NAME )));\n\n//..Set the name \n$model-\u003esetName( 'The name' );\n\n/**\n * Outputs:\n * array (size=1)\n *   'name' =\u003e string 'The name' (length=8)\n */\nvar_dump( $model-\u003etoArray());\n\n\n//..Set the name to \"foo\"\n$model-\u003esetName( 'foo' );\n\n/**\n * Outputs:\n * array (size=1)\n *   'name' =\u003e string 'bar' (length=3)\n */\nvar_dump( $model-\u003etoArray());\n\n//..Set to baz and an exception will be thrown \n//..Throws: \"baz\" of type \"buffalokiwi\\magicgraph\\property\\StringProperty\" is not a valid value for the \"name\" property.  \n//  Check any behavior callbacks, and ensure that the property is set to the correct type.  IPropertyBehavior::getValidateCallback() failed.\n//..This will also generate an error \"Behavior validation failure in closure: TestProperties in file XXX\"\n$model-\u003esetName( 'baz' );\n```\n\nWhen using any of the behavior callbacks, you can replace IModel and mixed types with any derived type.  It is also worth \nnoting that if you wanted a behavior to work with all properties, you can pass static::class as the property name to the \nPropertyBehavior constructor from GenericNamedPropertyBehavior.  This will only work for model level callbacks, and when\nthe strategy class name matches the supplied property name, the strategy is applied to every property in the model.\n\n\n---\n\n\n## Database Connections\n\nMagic Graph provides a simple abstraction over the [PHP PDO library](https://www.php.net/manual/en/book.pdo.php).  First, \nlets go over the four interfaces, then we'll go over the MySQL implementation.\n\n\n**[IConnectionProperties](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-IConnectionProperties.html)** \n\nThe connection properties interface is used to define the criteria used to connect to some database engine.  You'll find \nsuper fancy methods like getHost() and getDSN().  Truly mind-blowing stuff here.  It has everything you'd expect in a property bag \nfor a database connection.  \n\n\n**[IConnectionFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-IConnectionFactory.html)**\n\nThe connection factory is exactly what seems.  This interface may go through a revision in the near future.  The concept is to\nhave a factory that creates database connections.  In it's current form, it is probably best that one factory provides\nconnections for one persistence type.  In the future, this interface will be revised to more easily support multiple persistence\ntypes in a single factory.  Note: This does support multiple types in a single factory, but it's not easy to work with.  For now, keep it one to one and it works nice.\n\n\n**[IDBConnection](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-IDBConnection.html)**\n\nThis is an interface for the PDO object that ships with PHP, but with one additional method: \n```php\nexecuteQuery( string $statement ) : Generator\n```\nexecuteQuery() is a simple way to execute a simple statement without parameters.  \n\n\n**[IPDOConnection](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-IPDOConnection.html)**\n\nIPDOConnection extends IDBConnection and it adds several methods to make it easier to work with common query types.  Let's take a look.\n\n\n**Delete**\n\nThe delete method is used to delete rows.  This method will only delete by primary key, and composite primary keys are supported.\n\n```php\n/**\n * Execute a delete query for a record using a compound key.\n * @param string $table table name\n * @param array $pkPairs primary key to value pairs \n * @param int $limit limit\n * @return int affected rows\n * @throws InvalidArgumentExcepton if table or col or id are empty or if col\n * contains invalid characters or if limit is not an integer or is less than\n * one\n * @throws DBException if there is a problem executing the query\n */\nfunction delete( string $table, array $pkCols, int $limit = 1 ) : int;\n\n//..Example:\n\n$affectedRows = delete( 'mytable', ['pkcol1' =\u003e 'value1', 'pkcol2' =\u003e 'value2'], 1 );\n\n//..Generates the statement:\n// delete from mytable where pkcol1=? and pkcol2=? limit 1;  \n```\n\n\n**Update**  \n\nUpdate updates matching rows in some table.  This is also matched by primary key, and composite keys are supported.\n\n```php\n/**\n * Build an update query using a prepared statement.\n * @param string $table Table name\n * @param array $pkPairs list of [primary key =\u003e value] for locating records to update.\n * @param array $pairs Column names and values map\n * @param int $limit Limit to this number\n * @return int the number of affected rows\n * @throws InvalidArgumentException\n * @throws DBException\n */\nfunction update( string $table, array $pkPairs, array $pairs, int $limit = 1 ) : int;\n\n//..Example\n\n$affectedRows = update( 'mytable', ['id' =\u003e 1], ['name' =\u003e 'foo', 'md5name:md5' =\u003e 'foo'], 1 );\n\n//..Generates the statement:\n// update mytable set name=?, md5name=md5(?) where id=? limit 1;\n```\nFunctions can be added to columns by appending ':func' to any column name.   Multiple functions can be chained like this: ':func1:func2'\n\n\n**Insert**\n\nInsert is similar to update except that it inserts new records!  Wooooooo! \n  \n```php\n/**\n * Build an insert query using a prepared statement.\n * This will work for most queries, but if you need to do something\n * super complicated, write your own sql...\n *\n *\n * @param string $table Table name\n * @param array $pairs Column names and values map\n * @return int last insert id for updates\n * @throws InvalidArgumentException\n * @throws DBException\n */\nfunction insert( string $table, array $pairs ) : string;    \n\n//..Example:\n$lastInsertId = insert( 'mytable', ['col1' =\u003e 'value1', 'col2:md5' =\u003e 'value2'] );\n\n//..generates statement:\n// insert into mytable (col1, col2) values(?,md5(?)); \n```  \n\n\n**Cursors**\n\nHave you ever wanted to iterate over each row in some table?  You're in luck! \n\n```php  \n/**\n * Creates a cursor over some result set \n * @param string $statement Statement \n * @param type $options Parameters\n * @param type $scroll Enable Scroll \n * @return Generator Results \n */\nfunction forwardCursor( string $statement, $options = null, $scroll = false ) : Generator;  \n\n//..Use it like this:\n\nforeach( forwardCursor( 'select * from mytable where col=?', ['foo'] ) as $row )\n{\n  //..Do something with $row\n  //..$row is an associative array containing column names and values.\n}\n```  \n\nNote: The $scroll argument is deprecated and will be removed in a future release.  Scroll was supposed to allow bidirectional \ncursor movement, but not all drivers support scrollable cursors (mysql does not) and therefore $scroll should not be \nincluded in a generic interface.\n\n\n**Select**\n\nSurprise!  We can select things too!  Pass your statement and bindings to the select method, and BAM! you get results.\n  \n```php\n/**\n * Select some stuff from some database\n * @param string $statement sql statement\n * @param type $opt Bindings for prepared statement.  This can be an object or an array \n */ \nfunction select( string $statement, $opt = null ) : \\Generator;  \n\n//..Use like this:\n\nforeach( select( 'select * from mytable where col=?', ['foo'] ) as $row )\n{\n  //..Do something with $row\n  //..$row is an associative array containing column names and values.\n}\n\n//..Generates the statement:\n//  select * from mytable where col=?\n```\n\n\n**Select Multiple Result Sets**\n\nQueries that return multiple result sets are also fully supported.  This can be a stored procedure that returns multiple\nresult sets or simply adding semicolons between the statements.  Be careful with this one.  Semicolons can do nasty things.\n```php\n/**\n * Execute a sql statement that has multiple result sets\n * ie: a stored procedure that has multiple selects, or one of those snazzy\n * subquery statements\n * @param string $sql SQL statement to execute\n * @param array $bindings Column bindings \n * @return Generator array results\n * @throws DBException if there is one\n */\npublic function multiSelect( string $sql, array $bindings = [] ) : Generator\n//..Example:\n\nforeach( multiSelect( 'select * from mytable where id=?; select * from mytable where id=?', [1,2] ) as $rowSet )\n{\n  //..Each $rowSet entry contains a set of rows to iterate over.\n  foreach( $rowSet as $row )\n  {\n    //..$row is an associative array of column =\u003e value \n  }\n}\n```\n\n\n**Execute**\n\nExecutes some arbitrary statement without a result set.  \n```php\n/**\n * Executes a query with no result set.\n * @param string $statement Statement to execute \n * @param array $opt Map of bindings \n * @return int\n */\nfunction execute( string $statement, $opt = null ) : int;\n```\n\n\n### MySQL PDO \n\nMagic Graph currently ships with a single database adapter for MySQL, [MariaDBConnection](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-pdo-MariaDBConnection.html), which \nextends the abstract base class PDOConnection, implements IPDOConnection, and adds the necessary driver-specific sql statements.  This is the PDO implementation to \nuse for all things MySQL/MariaDB.\n\n\n### Connection Factories\n\nConnection factories generate database connections for use with some driver.  I'm sure you've seen the examples throughout this readme, but in case you haven't, here it is:\n```php\n$dbFactory = new buffalokiwi\\magicgraph\\pdo\\PDOConnectionFactory( //..A factory for managing and sharing connection instances \n  new buffalokiwi\\magicgraph\\pdo\\MariaConnectionProperties(       //..Connection properties for MariaDB / MySQL\n    'localhost',                  //..Database server host name \n    'root',                       //..User name\n    '',                           //..Password\n    'fancydatabase' ),            //..Database \n  //..This is the factory method, which is used to create database connection instances\n  //..The above-defined connection arguments are passed to the closure.\n  function( buffalokiwi\\magicgraph\\pdo\\IConnectionProperties $args  ) { \n    //..Return a MariaDB connection \n    return new buffalokiwi\\magicgraph\\pdo\\MariaDBConnection( $args );\n  }\n);\n```\n\nThe idea is to create a factory using some connection properties, and have that generic factory return a PDO implementation of the correct type.  Nothing ground breaking here.\n\n\n---\n\n\n## Working with Currency\n\nCurrency is something that doesn't always work properly.  There are many ways to solve the currency problem (which we will not discuss here) \nFortunately we have this awesome library [MoneyPHP](https://github.com/moneyphp/money),which is based on [Martin Fowler's money pattern](https://martinfowler.com/eaaCatalog/money.html), uses\nstrings internally to represent currency, and the best part is that money objects are immutable.  \n\nOne downside to MoneyPHP is that it does not have any type of interface.  It is simply the Money object.  In Magic Graph, \nthere is an interface for Money [IMoney](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-money-IMoney.html), which is implemented by [MoneyProxy](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-money-MoneyProxy.html), which accepts an instance of [Money](https://github.com/moneyphp/money/blob/master/src/Money.php) and \nproxies all calls to the underlying Money object.  This is because at some point we may want to swap out MoneyPHP for some other library, and we can't do that without a proper abstraction.\n\nSince MoneyPHP handles different currencies and formats, we need a money factory to have an easy way of generating money instances of the same currency.  In Magic Graph, we have a factory\n[MoneyFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-money-MoneyFactory.html), which implements [IMoneyFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-money-IMoneyFactory.html).\n\nHere's an example of how to set up a money factory for US Dollars\n\n```php\n$currencies = new \\Money\\Currencies\\ISOCurrencies();\n\n//..Money formatter \n$intlFmt = new Money\\Formatter\\IntlMoneyFormatter( \n  new \\NumberFormatter( 'en_US', \\NumberFormatter::CURRENCY ), \n  $currencies \n);\n\n$decFmt = new Money\\Formatter\\DecimalMoneyFormatter( $currencies );\n\n//..Money factory \n//..This is used to lock the system down to a certain type of currency, \n// and to provide an abstract wrapper for the underlying money implementation.\n$dollarFactory = new \\buffalokiwi\\magicgraph\\money\\MoneyFactory( \n  function( string $amount ) use($intlFmt,$decFmt) : buffalokiwi\\magicgraph\\money\\IMoney {\n    return new buffalokiwi\\magicgraph\\money\\MoneyProxy( \n      Money::USD( $amount ), \n      $intlFmt, \n      $decFmt );\n  }\n);   \n```\n\nNow we can create money and format it for the configured currency:\n\n```php\n$treeFiddy = $dollarFactory-\u003egetMoney( '3.50' );\n\n/**\n * Outputs:\n * object(buffalokiwi\\magicgraph\\money\\MoneyProxy)[600]\n *  private 'money' =\u003e \n *    object(Money\\Money)[601]\n *      private 'amount' =\u003e string '350' (length=3)\n *      private 'currency' =\u003e \n *        object(Money\\Currency)[602]\n *          private 'code' =\u003e string 'USD' (length=3)\n *  private 'formatter' =\u003e \n *    object(Money\\Formatter\\IntlMoneyFormatter)[595]\n *      private 'formatter' =\u003e \n *        object(NumberFormatter)[596]\n *      private 'currencies' =\u003e \n *        object(Money\\Currencies\\ISOCurrencies)[594]\n *  private 'decFmt' =\u003e \n *    object(Money\\Formatter\\DecimalMoneyFormatter)[597]\n *      private 'currencies' =\u003e \n *        object(Money\\Currencies\\ISOCurrencies)[594]\n */\nvar_dump( $treeFiddy );\n\n//..Outputs: 3.50\necho (string)$treeFiddy;\n\n//..Outputs: $3.50\necho $treeFiddy-\u003egetFormattedAmount();\n```\n\nUsing currency properties in Magic Graph is easy.  Simply use the 'money' property type in your property configuration arrays.\n\nNote: Due to the use of MoneyFactory, a service locator will need to be passed to the config mapper.  This will allow the config mapper\nto find the money factory (and other things) when creating property objects.  See [Magic Graph Setup](#the-config-mapper) for more information.  Here's a \nquick example for reference:\n\n```php\n//..Service locator \n$ioc = new buffalokiwi\\buffalotools\\ioc\\IOC();\n\n//..Default Magic Graph Configuration Mapper. \n//..This creates the property objects.\n$configMapper = new buffalokiwi\\magicgraph\\property\\DefaultConfigMapper( $ioc );\n\n//..Factory wraps the config mapper and can combine config arrays.  \n//  Uses the config mapper to produce properties.\n$propertyFactory = new \\buffalokiwi\\magicgraph\\property\\PropertyFactory( $configMapper );\n\n//..Use $propertyFactory to create instances of IPropertySet\n```\n\n\n---\n\n\n## Creating HTML Elements\n\nThis package should not have been part of Magic Graph, and instead should have been released as a separate extension.  However, the package is here and it\nis fully integrated with properties, and therefore it's not going anywhere.\n\nBy using [IElementFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-htmlproperty-IElementFactory.html) implementations, it is possible\nto convert IProperty instances to [IElement](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-htmlproperty-IElement.html) and eventually to a string containing the HTML. \n\nThe default implementation of IElementFactory is [ElementFactory](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-htmlproperty-ElementFactory.html), which accepts\na list of [IElementFactoryComponent](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-htmlproperty-IElementFactoryComponent.html).  IELementFactoryComponent instances are used\nto map a subclass of IProperty to a function resposible for converting the IProperty instance to an instance of IElement.  \n\nThere is a default mapping class called [DefaultComponentMap](https://sixarmdonkey.github.io/magicgraph/classes/buffalokiwi-magicgraph-property-htmlproperty-DefaultComponentMap.html), which can be used \nto quickly get started with ElementFactory.\n\nHere's an example:\n\n```php\n//..Create a simple model with a few properties\n$model = new buffalokiwi\\magicgraph\\QuickModel([\n  'numberinput' =\u003e ['type' =\u003e 'int'],\n  'stringinput' =\u003e ['type' =\u003e 'string'],\n  'dateinput' =\u003e ['type' =\u003e 'date'],\n  'boolinput' =\u003e ['type' =\u003e 'bool'],\n  'enuminput' =\u003e ['type' =\u003e 'rtenum', 'config' =\u003e ['test1','test2','test3'], 'value' =\u003e 'test1']    \n]);\n\n\n$elementFactory = new buffalokiwi\\magicgraph\\property\\htmlproperty\\ElementFactory( ...( new buffalokiwi\\magicgraph\\property\\htmlproperty\\DefaultComponentMap())-\u003egetMap());\n\nforeach( $model-\u003egetPropertySet()-\u003egetProperties() as $prop )\n{\n  echo $elementFactory-\u003ecreateElement( $model, $prop, $prop-\u003egetName(), null, (string)$model-\u003egetValue( $prop-\u003egetName()))-\u003ebuild();\n  echo '\u003cbr /\u003e';\n}\n```\n\nThe above-example will generate five html inputs:\n\n```html\n\u003cinput step=\"1\" type=\"number\" value=\"0\" name=\"numberinput\" id=\"numberinput\" /\u003e\n\u003cbr /\u003e\n\u003cinput type=\"text\" name=\"stringinput\" id=\"stringinput\" /\u003e\n\u003cbr /\u003e\n\u003cinput type=\"date\" name=\"dateinput\" id=\"dateinput\" /\u003e\n\u003cbr /\u003e\n\u003clabel\u003e\u003cinput type=\"checkbox\" class=\" checkbox\" name=\"boolinput\" id=\"boolinput\" /\u003e\u003cspan\u003e\u003c/span\u003e\u003c/label\u003e\n\u003cbr /\u003e\n\u003cselect name=\"enuminput\" id=\"enuminput\" \u003e\n  \u003coption value=\"test1\" selected=\"selected\" \u003eTest1\u003c/option\u003e\n  \u003coption value=\"test2\" \u003eTest2\u003c/option\u003e\n  \u003coption value=\"test3\" \u003eTest3\u003c/option\u003e\n\u003c/select\u003e\n\u003cbr /\u003e\n```\n\n\nIf you want to add element factory components for any custom properties, or if you want to override the default \ncomponents, you can pass this to the constructor of DefaultComponentMap.  Any matching properties are internally overridden.\n\nFor example, while this is the default handler for IStringProperty, it could be overridden if passed to the constructor.\n```php\nnew buffalokiwi\\magicgraph\\property\\htmlproperty\\DefaultComponentMap([\n  buffalokiwi\\magicgraph\\property\\IStringProperty::class =\u003e function( \n    buffalokiwi\\magicgraph\\property\\IStringProperty $prop, \n    string $name, \n    ?string $id, \n    string $value \n  ) : buffalokiwi\\magicgraph\\property\\htmlproperty\\IElement {\n    $attrs = [];\n\n    if ( $prop-\u003egetMin() != -1 )\n      $attrs['minlength'] = $prop-\u003egetMin();\n\n    if ( $prop-\u003egetMax() != -1 )\n      $attrs['maxlength'] = $prop-\u003egetMax();\n\n    if ( !empty( $prop-\u003egetPattern()))\n      $attrs['pattern'] = $prop-\u003egetPattern();\n\n    if ( $prop-\u003egetFlags()-\u003ehasVal( \\buffalokiwi\\magicgraph\\property\\IPropertyFlags::REQUIRED ))\n      $attrs['required'] = 'required';\n\n    if ( $prop-\u003egetMax() != -1 \u0026\u0026 $prop-\u003egetMax() \u003e 255 )\n      return new buffalokiwi\\magicgraph\\property\\htmlproperty\\TextAreaElement( $name, $id, $value, $attrs );\n    else\n      return new \\buffalokiwi\\magicgraph\\property\\htmlproperty\\InputElement( 'text', $name, $id ?? '', $value, $attrs );\n  }\n]);\n```\n\nThe definition for the callbacks is as follows:\n\n```php\n/**\n * Converts IProperty to IElement \n * @param \\buffalokiwi\\magicgraph\\property\\IProperty $prop Property to convert\n * @param string $name property/html form input name \n * @param string|null $id html element id attribute value \n * @param string $value Property value as a string \n * @return buffalokiwi\\magicgraph\\property\\htmlproperty\\IElement HTML Element \n */\nfunction( \\buffalokiwi\\magicgraph\\property\\IProperty $prop, string $name, \n  ?string $id, string $value ) : buffalokiwi\\magicgraph\\property\\htmlproperty\\IElement;\n```\n\n\n---\n\n\n\n## Magic Graph Setup\n\nMagic Graph was designed to support the [composition root pattern](https://medium.com/@cfryerdev/dependency-injection-composition-root-418a1bb19130). \nThe idea is to have every call to \"new object\" in a single file called composition root.  While Magic Graph object instantiation may look complicated, \nyou only have to write that code once, and all of it ends up in one place.  Instances of various Magic Graph components are then injected into other classes as a dependency. \n\nHere's what the composition root section for magic graph may look like.\n\n1. Create the service locator container.  This is used to provide various factories (like DateFactory and MoneyFactory) to the config mapper.\n2. Create a database connection factory.  This will be used by repositories.\n3. Add DateFactory to the container.  This is used within IDateProperty.\n4. Add MoneyFactory to the container.  This is used within IMoneyProperty.\n5. Create the config mapper.  This is an instance of IConfigMapper, and is responsible for creating instances of IProperty based on types listed in the property configuration arrays.\n6. Create the PropertyFactory instance.  This creates IPropertySet instances with the appropriate IConfigMapper.  Property sets contain the properties used by IModel instances.\n7. Add ITransactionFactory to the container.  This will be used by various relationship providers and things that need to unify saves across multiple repositories.\n8. Optionally create local variables for IDBConnection and IDBFactory.  This can make writing composition root a little easier and reduce calls to the container.\n\n\n```php\n\n/*********************/\n/* IoC Container     */\n/*********************/\n\n$ioc = new \\buffalokiwi\\buffalotools\\ioc\\IOC();\n\n\n/**********************/\n/* Database           */\n/**********************/\n\n$ioc-\u003eaddInterface(buffalokiwi\\magicgraph\\pdo\\IConnectionFactory::class, function() {\n  return new \\buffalokiwi\\magicgraph\\pdo\\PDOConnectionFactory( \n    new buffalokiwi\\magicgraph\\pdo\\MariaConnectionProperties( \n      'localhost',    //..Host\n      'root',         //..User\n      '',             //..Pass\n      'magicgraph' ), //..Database \n   function(\\buffalokiwi\\magicgraph\\pdo\\IConnectionProperties $args  ) {\n     return new buffalokiwi\\magicgraph\\pdo\\MariaDBConnection( $args, function(buffalokiwi\\magicgraph\\pdo\\IDBConnection $c ) { $this-\u003ecloseConnection($c); });\n   });                \n});\n\n\n/**********************/\n/* Dates              */\n/**********************/\n\n$ioc-\u003eaddInterface( \\buffalokiwi\\buffalotools\\date\\IDateFactory::class, function() { \n  return new \\buffalokiwi\\buffalotools\\date\\DateFactory();   \n});\n\n\n\n/*********************/\n/* Money Factory     */\n/*********************/\n\n$ioc-\u003eaddInterface( \\buffalokiwi\\magicgraph\\money\\IMoneyFactory::class, function() {\n  \n  $currencies = new Money\\Currencies\\ISOCurrencies();\n  //..Money formatter \n  $intlFmt = new \\Money\\Formatter\\IntlMoneyFormatter( \n    new \\NumberFormatter( 'en_US', \\NumberFormatter::CURRENCY ), \n    $currencies );\n\n  $decFmt = new \\Money\\Formatter\\DecimalMoneyFormatter( $currencies );\n\n  //..Money factory \n  //..This is used to lock the system down to a certain type of currency, \n  // and to provide an abstract wrapper for the underlying money implementation.\n  return new \\buffalokiwi\\magicgraph\\money\\MoneyFactory( function( string $amount ) use($intlFmt,$decFmt) : \\buffalokiwi\\magicgraph\\money\\IMoney {\n    return new \\buffalokiwi\\magicgraph\\money\\MoneyProxy( \\Money\\Money::USD( $amount ), $intlFmt, $decFmt );\n  });\n});\n\n\n/*********************/\n/* Magic Graph Setup */\n/*********************/\n\n//..Converts IPropertyConfig config arrays into properties\n//..If creating custom propeties, this must be replaced with a custom implementation.\n$configMapper = new buffalokiwi\\magicgraph\\property\\DefaultConfigMapper( $ioc );\n\n//..Factory wraps the config mapper and can combine config arrays.  \n//  Uses the config mapper to produce properties.\n$propertyFactory = new \\buffalokiwi\\magicgraph\\property\\PropertyFactory( $configMapper );\n\n//..The property set factory is required when service providers augment the model configuration.  This is used for \n//  things like the EAV system.\n//..The closure is provided with a list of IPropertyConfig instances which are\n//  supplied by the various service providers and base config.\n$ioc-\u003eaddInterface( \\buffalokiwi\\magicgraph\\property\\IPropertySetFactory::class, function() use ($propertyFactory) {\n  return new \\buffalokiwi\\magicgraph\\property\\PropertySetFactory(\n    $propertyFactory, \n    function(\\buffalokiwi\\magicgraph\\property\\IPropertyFactory $factory, \\buffalokiwi\\magicgraph\\property\\IPropertyConfig ...$config ) {\n      return new DefaultPropertySet( $factory, ...$config );\n  });\n});\n\n//..Transaction factory is used to handle saving multiple things at one time\n$ioc-\u003eaddInterface( \\buffalokiwi\\magicgraph\\persist\\ITransactionFactory::class, function() {\n  return new \\buffalokiwi\\magicgraph\\persist\\DefaultTransactionFactory();\n});\n\n//..I like to set up a few shared variables to use in composition root for the database factory and default connection.\n//..Database connection factory \n$db = $ioc-\u003egetInstance( \\buffalokiwi\\magicgraph\\pdo\\IConnectionFactory::class );    \n/* @var $db \\buffalokiwi\\magicgraph\\pdo\\IConnectionFactory */\n\n//..Default shared db connection \n$dbc = $db-\u003egetConnection();\n/* @var $dbc \\buffalokiwi\\magicgraph\\pdo\\IDBConnection */\n\n\n```\n\nOnce Magic Graph has been initialized, you can start adding repositories to the container. \n\nThe following example is based on this table:\n```sql\ncreate table testtable ( id int auto_increment primary key, name varchar(50)) engine=innodb;\n```\n\n```php\n\n//..Test repository interface\n//..We always need a unique name for the service locator\ninterface ITestRepo extends \\buffalokiwi\\magicgraph\\persist\\IRepository {}\n\n//..Test repository implementation \nclass TestRepo extends \\buffalokiwi\\magicgraph\\persist\\SQLRepository implements ITestRepo {};\n\n//..Test model \nclass TestModel extends buffalokiwi\\magicgraph\\DefaultModel {}\n\n\n//..Add ITestRepo to the container \n$ioc-\u003eaddInterface( ITestRepo::class, function() use ($dbc,$propertyFactory) {\n  return new TestRepo(\n    'testtable',\n    new \\buffalokiwi\\magicgraph\\DefaultModelMapper( function( buffalokiwi\\magicgraph\\property\\IPropertySet $props ) {\n      return new TestModel( $props );\n    }, TestModel::class ),\n    $dbc,\n    new buffalokiwi\\magicgraph\\property\\DefaultPropertySet( \n      $propertyFactory, \n      new buffalokiwi\\magicgraph\\property\\QuickPropertyConfig([\n        'id' =\u003e ['type' =\u003e 'int', 'flags' =\u003e ['primary']], \n        'name' =\u003e ['type' =\u003e 'string']]))\n  );\n});\n\n\n//..And now if we wanted to use this\n$testRepo = $ioc-\u003egetInstance( ITestRepo::class );\n/* @var $testRepo \\buffalokiwi\\magicgraph\\persist\\IRepository */\n\n//..Create a new model\n$testModel = $testRepo-\u003ecreate();\n\n//..Set the name property\n$testModel-\u003ename = 'test';\n\n//..Save the model \n$testRepo-\u003esave( $testModel );\n\n//..Get the id of the new model\n//..Outputs \"1\" \necho $testModel-\u003eid;\n```\n\n\n---\n\n\n## Entity Attribute Value\n\nThis is the buffalokiwi\\magicgraph\\eav package.  \n\n\n## Searching\n\nThis is the buffalokiwi\\magicgraph\\search and buffalokiwi\\magicgraph\\eav\\search packages.  \n\nSearching works with relationships, etc.  It's baked into IRepository implementations and the query builders can be swapped\nout when the repo is constructed.\n\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsixarmdonkey%2Fmagicgraph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsixarmdonkey%2Fmagicgraph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsixarmdonkey%2Fmagicgraph/lists"}