2012-12-31

PHP Object Persistence

Documentation (together with my other libs)

0001.<?php
0002. 
0003. 
0004. 
0005.// --- INCLUDES --- //
0006.        require_once dirname(__FILE__).'/'.'inc.php4';
0007.        require_once dirname(__FILE__).'/'.'lib.cDBAccess_MySQL.php4';
0008.        // Event Logging
0009.        require_once dirname(__FILE__).'/'.'lib.cApp.php';
0010.        require_once dirname(__FILE__).'/'.'lib.cLog.php';
0011.        require_once dirname(__FILE__).'/'.'lib.cObjectPersistence_ColumnTypes.php';
0012. 
0013. 
0014. 
0015.// --- CONSTANTS --- //
0016. 
0017.        // Column types
0018.        define('DWOP_TYPE_STR',  1);
0019.        define('DWOP_TYPE_NUM',  2);
0020.        define('DWOP_TYPE_REAL', 3);
0021.        define('DWOP_TYPE_BOOL', 4);
0022.        define('DWOP_TYPE_ENUM', 5);
0023.        define('DWOP_TYPE_SET',  6);
0024. 
0025.        // Column flags
0026.        define('DWOP_NOT_NULL', 1);
0027.        define('DWOP_BINARY',   2);
0028.        //define('DWOP_BINARY',   2);
0029. 
0030.        // For cDwOpClass::GetObjectMultiIdString()
0031.        define('DWOP_VALUE_DUMP_MODE__SQL', 0);
0032.        define('DWOP_VALUE_DUMP_MODE__SQL_SHORT', 2);
0033.        define('DWOP_VALUE_DUMP_MODE__SQL_VALUES', 3);
0034.        define('DWOP_VALUE_DUMP_MODE__ESCAPED_VALUES', 4);
0035.        define('DWOP_VALUE_DUMP_MODE__COMA', 5);
0036. 
0037. 
0038./** *********************************************************************************
0039. <h1>  RDBMS Object Loader for PHP  </h1>
0040. 
0041.        @version 2.4.6
0042.        @author Ondra Zizka, Dynawest;  ondra@dynawest.cz
0043. 
0044.        <h2>Plan:</h2>
0045.        3.0.0:
0046.                - Add:      Complete cross-class references handling
0047.        2.x.x:
0048.                - Class::RemProperty() - handle removal of ID and KEY properties.
0049.                        Class::aoIdProperties - rewrite so that GetIdProperties etc will compute from properties themselves. ??? Good idea???
0050. 
0051.        <h2>History:</h2>
0052.        2.4.7:
0053.                - Fixed:  cDwOpProperty::FormatForSql() returns correctly for DWOP_TYPE_REAL column types.
0054.        2.4.7:
0055.                - Added:  DeleteObject() supports multi-property PRIMARY KEYS in it's second parameter
0056.        2.4.6:
0057.                - Added:  Object Pool supports multi-property PRIMARY KEYS
0058.        2.4.5:
0059.                - Fixed:  Fix in SaveObject() - now saves all properties that are set. Null values for NOT NULL columns converted to default.
0060.                - Added:  function GetNonKeyProperties()
0061.                - Added:  DeleteObject() now can take Class name and single ID as parameters.
0062.                - Added:  cDwOpProperty now remembers it's PRIMARY KEY properties added through AddIdProperty() etc.
0063.                - Added:  cDwOpProperty::IsKey()
0064.                - Changed:  cDwOpColumnType::GetDefaultDefault() is not abstract returns a value according to basic column type.
0065.                - Fixed:  DwFwIdAndProperties::HasProperty() now uses array_key_exists() instead of fucking isset().
0066.        2.4.4:
0067.                - Fixed:  Massive fix in SaveObject() and one in DeleteObject()
0068.                - Added:  param $sGlue to  GetIdPropertiesSql($oObject, $sGlue=', ')
0069.        2.4.3:
0070.                - Added:  Support for multi-property PRIMARY KEYs  in SaveObject(), DeleteObject()
0071.        2.4.2:
0072.                - Added:  DeleteObject($oObject);
0073.                - Added:  GetIdPropertiesSql()
0074.        2.4.1:
0075.                - Added:  param $bOverwrite to function AddObjectIntoPool( $oObject, $bOverwrite=true )
0076.        2.4.0:
0077.                - Added:    M : N cross-class references
0078.                  - Added:   function SetMnRelation($oClass1, $oClassGlue, $oClass2)
0079.                  - Added:   function LoadObjectsByMnRelation($oObject, $oClassGlue, $oClass2, $xId=null)
0080. 
0081.                - Added:   Support for multi-property PRIMARY key - but only for keeping it, not for loading or saving
0082. 
0083.                - Added:   Real column types
0084.                              (abstract class cDwOpColumnType_Real extends cDwOpColumnType) and derived classes)
0085.                - Added:   Ordinal column types can take possible values definiton as they go from the DB:
0086.                              "'value', 'value', ..."
0087.                - Added:   Column types BIGINT and MEDIUMINT
0088. 
0089.                Objects loading:
0090.                - Added:   function LoadObjects($oClass)
0091.                - Added:   function LoadObjectsBySql($xClass, $sSQL)
0092.                - Added:   function LoadObjectsFromResult($xClass, $oRes)
0093. 
0094.                Class related:
0095.          - Added:   function ConvertAndCheckClass( $oClass )
0096.                - Added:   function CreateClassCopy( $sName, $xClass )
0097.                - Added:   function CreateClassByTable( $sName, $sTable )
0098.                - Added:   function CreateColumnTypeObject($sTypeString, $bNull, $sDefault)
0099. 
0100. 
0101.        2.3.0:
0102.                - Added:    CreateClassByTable()
0103.                            Creates class definition according to a table in a database.
0104.        2.2.1:
0105.                - Added:    LoadObjectsFromResult().
0106.                - Changed:  LoadObjectsByValue() now uses LoadObjectsFromResult().
0107.        2.2.0:
0108.                - Added:    Ordering by properties support.
0109.                            GetOrderProperties(), SetOrderProperties()
0110.        2.1.0:
0111.                - Added:    Default values, better checking.
0112.                            GetDefault(), SetDefault(), GetDefaultDefault()
0113.        2.0.2:
0114.                - Added:    Load* functions' first parameter can be also a class name string.
0115.        2.0.1:
0116.                - Added:    GetPoolCount()
0117.                - Changed:  SaveObject() adds UPDATEd (overwrite) and INSERTed objects into pool
0118.        2.0.0:
0119.                - Add central object registry - object is loaded only once,
0120.                  then return the formerly loaded
0121.        1.1.0:
0122.                - Added support for AUTO_INCREMENT when creating objects.
0123.                - Added possibility to load objects by values.
0124. ***********************************************************************************/
0125.class cDwObjectPersistence_DB {
0126. 
0127.        var $oDB = null;
0128.        function &GetDB()    { return $this->oDB; }
0129. 
0130.        var $sError = null;
0131.        function GetError()       { return $this->sError; }
0132.        function SetError($sError){ $this->sError = $sError; }
0133. 
0134. 
0135.        function cDwObjectPersistence_DB($oDB){
0136.                $this->oDB = $oDB;
0137.        }
0138. 
0139. 
0140.        /**<***********************************************************************>
0141.        *  Class objects dictionary                                                *
0142.        ***************************************************************************/
0143.        var $aoClassesDictionary = Array();
0144.        function GetClassNames(){ return array_keys($this->aoClassesDictionary); }
0145.        function AddClassIntoDictionary( $oClass ){
0146.                $this->aoClassesDictionary[$oClass->GetName()] = $oClass;
0147.        }
0148.        function GetClassFromDictionary( $sClassName ){
0149.                return isset($this->aoClassesDictionary[$sClassName]) ? $this->aoClassesDictionary[$sClassName] : null;
0150.        }
0151. 
0152. 
0153.        /**<***********************************************************************>
0154.        *  Objects pool                                                            *
0155.        ***************************************************************************/
0156.        var $aaoObjectPool = Array();
0157. 
0158.        function AddObjectIntoPool( $oObject, $bOverwrite=true ){
0159.                if( !is_object($oObject) ) return false;
0160.                $sClassName = get_class($oObject);
0161.                //App::Log("\$sClassName: ".$sClassName);///
0162.                if( !$oClass = $this->GetClassFromDictionary($sClassName) ){ return false; }
0163. 
0164.                // Get the object unique PRIMARY KEYs string
0165.                //App::Log("GetIdPropertiesCount(): ".$oClass->GetIdPropertiesCount());///
0166.                switch( $oClass->GetIdPropertiesCount() ){
0167.                        case 0: $bUsePool = false; return false; break;
0168.                        case 1:  $xId = $oObject->GetId(); break;
0169.                        default: $xId = $oClass->GetObjectMultiIdString( $oObject, DWOP_VALUE_DUMP_MODE__SQL_VALUES ); break;
0170.                }
0171.                //if( 0 == $oClass->GetIdPropertiesCount() ) return false;
0172.                //$xId = $oClass->GetObjectMultiIdString( $oObject, DWOP_VALUE_DUMP_MODE__SQL_VALUES );
0173. 
0174.                // If we should not overwrite...
0175.                if( !$bOverwrite ){
0176.                        // and the object exists, don't write and return true.
0177.                        if( isset( $this->aaoObjectPool[get_class($oObject)][$xId] ) )
0178.                                return true;
0179.                }
0180. 
0181.                // Save the object handle.
0182.                $this->aaoObjectPool[get_class($oObject)][$xId] = $oObject;
0183.                return true;
0184.        }
0185. 
0186.        function AddObjectIntoPool_SingleId( $oObject, $bOverwrite=true ){
0187.                if( !is_object($oObject) ) return false;
0188. 
0189.                // If we should not overwrite...
0190.                if( !$bOverwrite ){
0191.                        // and the object exists, don't write and return true.
0192.                        if( isset( $this->aaoObjectPool[get_class($oObject)][$oObject->GetId()] ) )
0193.                                return true;
0194.                }
0195. 
0196.                // Save the object handle.
0197.                $this->aaoObjectPool[get_class($oObject)][$oObject->GetId()] = $oObject;
0198.                return true;
0199.        }
0200. 
0201.        function GetObjectFromPool( $sClassName, $xId ){
0202.                $oObject = null;
0203.                if( isset($this->aaoObjectPool[$sClassName][$xId]) ){
0204.                        $oObject = $this->aaoObjectPool[$sClassName][$xId];
0205.                        //echo "<pre><strong>Pool hit!</strong></pre>";///
0206.                }
0207.                return $oObject;
0208.        }
0209. 
0210. 
0211.        function RemObjectFromPool( $oObject ){
0212.                if( !is_object($oObject) ) return false;
0213.                // Get the Class object
0214.                $sClassName = get_class($oObject);
0215.                if( !($oClass = $this->GetClassFromDictionary($sClassName)) ){ return false; }
0216. 
0217.                // Get the object unique PRIMARY KEYs string
0218.                //App::Log("GetIdPropertiesCount(): ".$oClass->GetIdPropertiesCount());///
0219.                switch( $oClass->GetIdPropertiesCount() ){
0220.                        case 0: $bUsePool = false; return false; break;
0221.                        case 1:  $xId = $oObject->GetId(); break;
0222.                        default: $xId = $oClass->GetObjectMultiIdString( $oObject, DWOP_VALUE_DUMP_MODE__SQL_VALUES ); break;
0223.                }
0224.                // Remove the object with the acquired ID
0225.                if( isset( $this->aaoObjectPool[$sClassName][$xId] ) )
0226.                        unset($this->aaoObjectPool[$sClassName][$xId]);
0227.                return true;
0228.        }
0229. 
0230.        function RemObjectFromPool_SingleId( $oObject ){
0231.                if( !is_object($oObject) ) return false;
0232.                $sClassName = get_class($oObject);
0233.                $xId = $oObject->GetId();
0234.                if( isset( $this->aaoObjectPool[$sClassName][$xId] ) )
0235.                        unset($this->aaoObjectPool[$sClassName][$xId]);
0236.                return true;
0237.        }
0238. 
0239. 
0240.        function HasObjectInPool( $oObject ){
0241.                if( !is_object($oObject) ) return false;
0242.                // Get the Class object
0243.                $sClassName = get_class($oObject);
0244.                if( !($oClass = $this->GetClassFromDictionary($sClassName)) ){ return false; }
0245. 
0246.                // Get the object unique PRIMARY KEYs string
0247.                switch( $oClass->GetIdPropertiesCount() ){
0248.                        case 0: $bUsePool = false; break;
0249.                        case 1:  $xId = $oObject->GetId(); break;
0250.                        default: $xId = $oClass->GetObjectMultiIdString( $oObject, DWOP_VALUE_DUMP_MODE__SQL_VALUES ); break;
0251.                }
0252.                // $xId = $oObject->GetId();
0253. 
0254.                $xId = $oClass->GetObjectMultiIdString( $oObject, DWOP_VALUE_DUMP_MODE__SQL_VALUES );
0255. 
0256.                if( isset( $this->aaoObjectPool[get_class($oObject)][$xId] ) )
0257.                        return true;
0258.                return false;
0259.        }
0260. 
0261.        function HasObjectInPool_SingleId( $oObject ){
0262.                if( !is_object($oObject) ) return false;
0263.                $xId = $oObject->GetId();
0264.                if( isset( $this->aaoObjectPool[get_class($oObject)][$xId] ) )
0265.                        return true;
0266.                return false;
0267.        }
0268. 
0269. 
0270.        function GetPoolCount(){
0271.                return array_sum(array_map('Count', $this->aaoObjectPool));
0272.        }
0273. 
0274. 
0275. 
0276. 
0277. 
0278. 
0279.        /**<***********************************************************************>
0280.        *  Converts the class name to the DwOpClass object and checks the class.   *
0281.        ***************************************************************************/
0282.        function ConvertAndCheckClass( $oClass ){
0283. 
0284.                // $oClass can be either cDwOpClass object or a class name
0285.                if( !$oClass ){ $this->SetError("Bad param (null) \$oClass for ".__METHOD__." in ".CallInfo(-2)); return false; }
0286. 
0287.                // A string - try to find the Class in the Dictionary
0288.                if( is_string($oClass) ){
0289.                        $sClassName = $oClass;
0290.                        $oClass = $this->GetClassFromDictionary($sClassName);
0291.                        if( !is_object($oClass) ){ $this->SetError("Class [$sClassName] not found in dictionary."); return false; }
0292.                }
0293.                // Not a string - either the Class object or an object of some Class.
0294.                else{
0295.                        if( !is_object($oClass) ){ $this->SetError("Bad param (not string or object) \$oClass for ".__METHOD__." in ".CallInfo(-2)); return false; }
0296. 
0297.                        if( $oClass instanceof cDwOpClass ){
0298.                                $sClassName = $oClass->GetName();
0299.                        }else{
0300.                                // object of some Class - try to find the Class object.
0301.                                $sClassName = get_class($oClass);
0302.                                $oClass = $this->GetClassFromDictionary( $sClassName );
0303.                                if( !is_object($oClass) ){ $this->SetError("Object's Class [$sClassName] not found in dictionary."); return false; }
0304.                        }
0305.                }
0306. 
0307.                // Check class existence
0308.                if( !class_exists($sClassName) ){ $this->SetError("Non-existent PHP class [$sClassName]. in ".__METHOD__." in ".CallInfo(-2)); return false; }
0309. 
0310.                return $oClass;
0311.        }
0312. 
0313. 
0314.        /**<***********************************************************************>
0315.        *  Deletes an object with ID $iID  of the given class $oClass              *
0316.           Deletes the given object from the DB.
0317.        ***************************************************************************/
0318.        function DeleteObject($oObject, $xId=null){
0319.                //echo "<pre>\n class object: ".AdjustedPrintR($oClass)."</pre>";///
0320.                //if( !is_object( $oObject ) ) return false;
0321.                $bSucc = false;
0322.                do{
0323. 
0324.                        /*// Find the Class object in the Dictionary
0325.                        $sClassName = get_class($oObject);
0326.                        do{
0327.                                $oClass = $this->GetClassFromDictionary($sClassName);
0328.                                $sClassName = get_parent_class($sClassName);
0329.                        }while( $sClassName && !$oClass );
0330. 
0331.                        // Check class object //
0332.                        //if( !$oClass ){ $this->SetError("Class [$sClassName] not found in the dictionary in ".__METHOD__." @ ".__LINE__); break; }
0333.                        /*/
0334.                        if( !($oClass = $this->ConvertAndCheckClass($oObject)) ){ $this->SetError("!\$oClass in ".__METHOD__." @ ".__LINE__); break; }
0335.                        $sClassName = $oClass->GetName();
0336.                        /**/
0337. 
0338.                        // If $oObject is an instance of Class, get it's ID.
0339. 
0340.                        // If $oObject was just identification of a Class, create a temporary object with just IDs.
0341.                        if( !($oObject instanceof $sClassName) ){
0342.                                // Check the ID - false, null, or empty array? -> fail.
0343.                                if( !$xId ){ $this->SetError("Unknown object ID to delete in ".__METHOD__." @ ".__LINE__); break; }
0344. 
0345.                                // If $xId is multiple ID - associative array of ids
0346.                                if( is_array($xId) ){
0347.                                        $oObject = new $sClassName();
0348.                                        foreach( $xId as $k => $v )
0349.                                                $oObject->SetProperty($k, $v);
0350.                                }
0351.                                // Single ID
0352.                                else{
0353.                                        //$oObject = $this->LoadObjectById($oClass, $xId);
0354.                                        //if(!$oObject) { $this->SetError("!LoadObjectById(".$oClass->GetName().", $xId) in ".__METHOD__." @ ".__LINE__); break; }
0355.                                        $oObject = new $sClassName();
0356.                                        $oObject->SetId($xId);
0357.                                }
0358.                        }
0359. 
0360.                        // -- From here further, we have DwOpClass $oClass  and  it's (pseudo)instance $oObject. -- //
0361. 
0362. 
0363.                        // Remove from the Object Pool
0364.                        if( !$this->RemObjectFromPool($oObject) ){ $this->SetError("!RemObjectFromPool() in ".__METHOD__." @ ".__LINE__); }
0365. 
0366. 
0367.                        $sTable  = $oClass->GetTable();
0368. 
0369.                        /*/ For one ID (PRIMARY KEY) property only...
0370.                        $oIdProp = $oClass->GetIdProperty();
0371.                        $sColId  = $oIdProp->GetColName();
0372.                        $xId = $oIdProp->FormatForSql( $oObject->GetProperty($oIdProp->GetColName()) );
0373.                        $sSQL = "DELETE FROM $sTable WHERE $sColId = $xId";
0374.                        /*/
0375.                        // For all PRIMARY KEYs:
0376.                        $saxIdsCond = $oClass->GetIdPropertiesSql($oObject, ' AND ');
0377.                        $sSQL = "DELETE FROM $sTable WHERE $saxIdsCond";
0378.                        /**/
0379. 
0380.                        // Perform SQL query
0381.                        $oRes = $this->GetDB()->Execute($sSQL);
0382.                        if( !$oRes || !$oRes->IsOK() ){ $this->SetError("SQL error [".$oRes->GetError()."] SQL: [$sSQL]"); break; }
0383. 
0384.                        $bSucc = true;
0385.                }while(false);
0386.                return $bSucc;
0387.        }// cDwObjectPersistence_DB::DeleteObject()
0388. 
0389. 
0390.        /**<*********************************************************************************>
0391.        *  Deletes objects of the given class $oClass
0392.        *  with some property equal to the given value.
0393. 
0394.          @param oClass:         cDwOpClass object or a class name to find in dictionary.
0395.          @param sPropertyName:  Name of the property to compare with $xVal.
0396.          @param xVal:           Value to compare property with.
0397.                @returns array of objects with value $sPropertyName equal to $xVal.
0398.        *************************************************************************************/
0399.        function DeleteObjectsByValueBad($oClass, $sPropertyName, $xVal){
0400.                //echo "<pre>\n class object: ".AdjustedPrintR($oClass)."</pre>";///
0401. 
0402.                if( !( $oClass = $this->ConvertAndCheckClass($oClass) ) ) return false;
0403.                $sClassName = $oClass->GetName();
0404. 
0405. 
0406.                // Get the property by which to recognize the object to load
0407.                $oProp = $oClass->GetProperty($sPropertyName);
0408.                if( null == $oProp ){ $this->SetError("No such property in class [$sClassName]: [$sPropertyName]"); return false; }
0409. 
0410.                // Create SQL query
0411.                $sTable  = $oClass->GetTable();
0412.                $sCol    = $oProp->GetColName();
0413.                $oIdProp = $oClass->GetIdProperty();
0414.                $sColId  = $oIdProp->GetColName();
0415. 
0416.                $sOrderSql = $oClass->GetOrderPropertiesSql();   // ORDER BY part
0417.                $sSQL = "DELETE FROM $sTable WHERE $sCol = ".$oProp->FormatForSql($xVal)." ".$sOrderSql;
0418. 
0419.                // Perform SQL query
0420.                $oRes = $this->GetDB()->Execute($sSQL);
0421.                if( !$oRes || !$oRes->IsOK() ){ $this->SetError("SQL error [".$oRes->GetError()."] SQL: [$sSQL] in ".__METHOD__.' @ '.__LINE__); return false; }
0422. 
0423.                // TODO: Remove objects from Object Pool!
0424.                $this->RemObjectFromPool($oObject);
0425. 
0426.                return true;
0427.        }// LoadObjectsByValue($oClass, $sPropertyName, $xVal)
0428. 
0429. 
0430.        /**<***********************************************************************>
0431.        *  Second version                                                          *
0432.        ***************************************************************************/
0433.        function DeleteObjectsByValue($oClass, $sPropertyName, $xVal){
0434. 
0435.                $aoObjects = $this->LoadObjectsByValue($oClass, $sPropertyName, $xVal);
0436.                if(!is_array($aoObjects)) return false;
0437. 
0438.                foreach( $aoObjects as $oObject){
0439.                        $this->DeleteObject($oObject);
0440.                }
0441. 
0442.                return true;
0443.        }// LoadObjectsByValue($oClass, $sPropertyName, $xVal)
0444. 
0445. 
0446. 
0447. 
0448.        /**<***********************************************************************>
0449.        *  Loads an object with ID $iID  of the given class $oClass                *
0450.        ***************************************************************************/
0451.        function LoadObjectById($oClass, $iID){
0452.                //echo "<pre>\n class object: ".AdjustedPrintR($oClass)."</pre>";///
0453. 
0454. 
0455.                /*/ First parameter can be either cDwOpClass object or a class name
0456.                if( is_string($oClass) ){
0457.                        $sClassName = $oClass;
0458.                        $oClass = $this->GetClassFromDictionary($sClassName);
0459.                        if( !$oClass ) return false;
0460.                }else{
0461.                        $sClassName = $oClass->GetName();
0462.                }
0463.                // Check class existence
0464.                if( !class_exists($sClassName) )
0465.                        return false;
0466.                /*/
0467.                if( !( $oClass = $this->ConvertAndCheckClass( $oClass ) ) ) return false;
0468.                $sClassName = $oClass->GetName();/**/
0469. 
0470. 
0471.                // Look for the object in the pool
0472.                if( $oObject = $this->GetObjectFromPool($sClassName, $iID) )
0473.                        return $oObject;
0474. 
0475. 
0476. 
0477.                $sTable  = $oClass->GetTable();
0478.                $oIdProp = $oClass->GetIdProperty();
0479.                $sColId  = $oIdProp->GetColName();
0480. 
0481.                // Create and perform SQL query
0482.                $sSQL = "SELECT * FROM $sTable WHERE $sColId = ".$oIdProp->FormatForSql($iID);
0483.                $oRet = $this->GetDB()->Select($sSQL);
0484.                if( !$oRet->IsOK() ) return false;
0485.                if( 0 == $oRet->NumRows() ) return null;
0486. 
0487.                $aData = $oRet->FetchRow(MYSQL_ASSOC);
0488. 
0489.                // Create the object
0490.                $oObject = new $sClassName();
0491. 
0492.                // Load all declared properties
0493.                foreach( $oClass->GetProperties() as $oProperty ){
0494.                        //echo "<div>\$oObject->SetProperty( ".$oProperty->GetColName().", \$aData[".$oProperty->GetColName()."] );</div>";///
0495.                        $oObject->SetProperty( $oProperty->GetColName(), $oProperty->GetColType()->ConvertFromResult( $aData[$oProperty->GetColName()] ) );
0496.                }
0497. 
0498.                $this->AddObjectIntoPool($oObject);
0499.                return $oObject;
0500.        }// LoadObjectById($oClass, $iID)
0501. 
0502. 
0503. 
0504. 
0505. 
0506.        /**<***********************************************************************************>
0507.        *  Loads all objects of the given class $oClass.
0508. 
0509.          @param oClass:         cDwOpClass object or a class name to find in dictionary.
0510.                @returns array of objects of the specified class.
0511.        ***************************************************************************************/
0512.        function LoadObjects($oClass){
0513. 
0514.                /*/ First parameter can be either cDwOpClass object or a class name
0515.                if( is_string($oClass) ){
0516.                        $sClassName = $oClass;
0517.                        $oClass = $this->GetClassFromDictionary($sClassName);
0518.                        if( !is_object($oClass) ){ $this->SetError("Class [$sClassName] not found in dictionary."); return false; }
0519.                }else{
0520.                        $sClassName = $oClass->GetName();
0521.                }
0522. 
0523.                // Check class existence
0524.                if( !class_exists($sClassName) ){ $this->SetError("Non-existent class [$sClassName]."); return false; }
0525.                /*/
0526.                if( !( $oClass = $this->ConvertAndCheckClass( $oClass ) ) ) return false;
0527.                $sClassName = $oClass->GetName();/**/
0528. 
0529. 
0530.                // Create SQL query
0531.                $sTable    = $oClass->GetTable();
0532.                $sOrderSql = $oClass->GetOrderPropertiesSql();   // ORDER BY part
0533.                $sSQL = "SELECT * FROM $sTable ".$sOrderSql;
0534. 
0535.                // Perform SQL query
0536.                $oRes = $this->GetDB()->Select($sSQL);
0537.                if( !$oRes || !$oRes->IsOK() ){
0538.                        $this->SetError("SQL error [".$oRes->GetError()."] SQL: [$sSQL]");
0539.                        return false;
0540.                }
0541. 
0542.                // Load Objects
0543.                $aoObjects = $this->LoadObjectsFromResult($oClass, $oRes);
0544.                $oRes->FreeResult();
0545. 
0546.                return $aoObjects;
0547. 
0548.        }// cDwObjectPersistence_DB::LoadObjects()
0549. 
0550. 
0551. 
0552.        /**<*********************************************************************************>
0553.        *  Loads an objects of the given class $oClass
0554.        *  with some property equal to the given value.
0555. 
0556.          @param oClass:         cDwOpClass object or a class name to find in dictionary.
0557.          @param sPropertyName:  Name of the property to compare with $xVal.
0558.          @param xVal:           Value to compare property with.
0559.                @returns array of objects with value $sPropertyName equal to $xVal.
0560.        *************************************************************************************/
0561.        function LoadObjectsByValue($oClass, $sPropertyName, $xVal){
0562.                //echo "<pre>\n class object: ".AdjustedPrintR($oClass)."</pre>";///
0563. 
0564. 
0565.                /*/ First parameter can be either cDwOpClass object or a class name
0566.                if( is_string($oClass) ){
0567.                        $sClassName = $oClass;
0568.                        $oClass = $this->GetClassFromDictionary($sClassName);
0569.                        if( !is_object($oClass) ){
0570.                                $this->SetError("Class [$sClassName] not found in dictionary.");
0571.                                return false;
0572.                        }
0573.                }else{
0574.                        $sClassName = $oClass->GetName();
0575.                }
0576. 
0577.                // Check class existence
0578.                if( !class_exists($sClassName) ){ $this->SetError("Non-existent class [$sClassName]."); return false; }
0579.                /*/
0580.                if( !( $oClass = $this->ConvertAndCheckClass( $oClass ) ) ) return false;
0581.                $sClassName = $oClass->GetName();/**/
0582. 
0583. 
0584.                // Get the property by which to recognize the object to load
0585.                $oProp = $oClass->GetProperty($sPropertyName);
0586.                if( null == $oProp ){
0587.                        $this->SetError("No such property in class [$sClassName]: [$sPropertyName]");
0588.                        return false;
0589.                }
0590. 
0591.                // Create SQL query
0592.                $sTable  = $oClass->GetTable();
0593.                $sCol    = $oProp->GetColName();
0594.                $oIdProp = $oClass->GetIdProperty();
0595.                $sColId  = $oIdProp->GetColName();
0596. 
0597.                $sOrderSql = $oClass->GetOrderPropertiesSql();   // ORDER BY part
0598.                $sSQL = "SELECT * FROM $sTable WHERE $sCol = ".$oProp->FormatForSql($xVal)." ".$sOrderSql;
0599. 
0600.                /*// Perform SQL query
0601.                $oRes = $this->GetDB()->Select($sSQL);
0602.                if( !$oRes || !$oRes->IsOK() ){
0603.                        $this->SetError("SQL error [".$oRes->GetError()."] SQL: [$sSQL]");
0604.                        return false;
0605.                }
0606. 
0607.                // Load Objects
0608.                $aoObjects = $this->LoadObjectsFromResult($oClass, $oRes);
0609.                $oRes->FreeResult();/*/
0610. 
0611.                $aoObjects = $this->LoadObjectsBySql($oClass, $sSQL);/**/
0612. 
0613.                return $aoObjects;
0614.        }// LoadObjectsByValue($oClass, $sPropertyName, $xVal)
0615. 
0616. 
0617. 
0618. 
0619. 
0620.        /**<*********************************************************************************>
0621.        *  Performs SQL query and converts the result to an array of objects.                *
0622.        *************************************************************************************/
0623.        function LoadObjectsBySql($xClass, $sSQL){
0624. 
0625.                // Perform SQL query
0626.                $oRes = $this->GetDB()->Select($sSQL);
0627.                if( !$oRes || !$oRes->IsOK() ){
0628.                        $this->SetError("SQL error [".$oRes->GetError()."] SQL: [$sSQL]");
0629.                        return false;
0630.                }
0631. 
0632.                // Load Objects
0633.                $aoObjects = $this->LoadObjectsFromResult($xClass, $oRes);
0634.                $oRes->FreeResult();
0635. 
0636.                return $aoObjects;
0637.        }
0638. 
0639. 
0640. 
0641.        /**<*********************************************************************************>
0642.        *  Converts the result to an array of objects.                                       *
0643.        *************************************************************************************/
0644.        function LoadObjectsFromResult($xClass, $oRes){
0645. 
0646.                // Convert and check the class
0647.                if( !( $oClass = $this->ConvertAndCheckClass( $xClass ) ) ){
0648.                        $this->SetError("!ConvertAndCheckClass(".gettype($xClass)."): ".$this->GetError()); return false;
0649.                }
0650.                $sClassName = $oClass->GetName();
0651. 
0652. 
0653.                $aoObjects = Array();
0654.                //if( 0 == $oRes->NumRows() ) return $aoObjects;
0655. 
0656.                // Class Name
0657.                //if( !is_object($oClass) ) echo CallInfo(-2);
0658. 
0659. 
0660. 
0661.                $aoPropertiesToTraverse = Array();
0662.                // Get the list of the properties that will be set from the result.
0663.                // Check column existence for all declared properties
0664.                $aoFields = $oRes->GetColumns();
0665.                $absFieldsContained = Array();
0666.                foreach( $aoFields as $oField ){ $absFieldsContained[$oField->name] = 1; }
0667.                //App::Log("\$absFieldsContained: ".AdjustedPrintR($absFieldsContained));///
0668.                foreach( $oClass->GetProperties() as $oProperty ){
0669.                        $sColName = $oProperty->GetColName();
0670.                        if( !array_key_exists($sColName, $absFieldsContained) ){
0671.                                $this->SetError("Warn: Column [$sColName] for property [".$oProperty->GetName()."] is not set; in ".__METHOD__." in ".CallInfo(-2));
0672.                                App::Log("Warn: Column [$sColName] for property [".$oProperty->GetName()."] is not set; in ".__METHOD__); continue;
0673.                        }
0674.                        $aoPropertiesToTraverse[] = $oProperty;
0675.                }/**/
0676. 
0677.                // Create the objects. For each row:
0678.                while( $aData = $oRes->FetchRow(MYSQL_ASSOC) ){
0679.                        $oObject = new $sClassName();
0680.                        /*// Load all declared properties
0681.                        foreach( $oClass->GetProperties() as $oProperty ){
0682.                                //echo "<div>\$oObject->SetProperty( ".$oProperty->GetColName().", \$aData[".$oProperty->GetColName()."] );</div>";///
0683.                                $sColName = $oProperty->GetColName();
0684.                                //if( !isset( $aData[$sColName] ) && !is_null($aData[$sColName]) ){
0685.                                if( !array_key_exists($sColName, $aData) ){
0686.                                        App::Log("Warn: Column [$sColName] for property [".$oProperty->GetName()."] is not set."); continue;
0687.                                } // DONE: Move before the while - fetch_fields()
0688.                                $oObject->SetProperty( $oProperty->GetColName(), $aData[$sColName] );
0689.                        }/**/
0690.                        // Load properties that are present in the result
0691.                        foreach( $aoPropertiesToTraverse as $oProperty ){
0692.                                //echo "<div>\$oObject->SetProperty( ".$oProperty->GetColName().", \$aData[".$oProperty->GetColName()."] );</div>";///
0693.                                $sColName = $oProperty->GetColName();
0694.                                $oObject->SetProperty( $sColName, $oProperty->GetColType()->ConvertFromResult($aData[$sColName]) );
0695.                        }
0696. 
0697.                        // Get object's unique ID, if possible
0698.                        $bUsePool = true;
0699.                        //App::Log("GetIdPropertiesCount(): ".$oClass->GetIdPropertiesCount());///
0700.                        switch( $oClass->GetIdPropertiesCount() ){
0701.                                case 0: $bUsePool = false; break;
0702.                                case 1:  $xId = $oObject->GetId(); break;
0703.                                default: $xId = $oClass->GetObjectMultiIdString( $oObject, DWOP_VALUE_DUMP_MODE__SQL_VALUES ); break;
0704.                        }
0705. 
0706.                        // Look for the object in the pool
0707.                        if(!$bUsePool)
0708.                                $aoObjects[] = $oObject;
0709.                        else
0710.                        if( $oObjectFromPool = $this->GetObjectFromPool($sClassName, $xId) ){
0711.                                //App::Log("Pool hit [$sClassName, ".$xId."]");///
0712.                                $aoObjects[] = $oObjectFromPool;
0713.                        }else{
0714.                                //App::Log("Pool miss [$sClassName, ".$xId."]");///
0715.                                $aoObjects[] = $oObject;
0716.                                $this->AddObjectIntoPool($oObject);
0717.                        }
0718. 
0719.                }// while( for each row in result )
0720. 
0721.                return $aoObjects;
0722. 
0723.        }// cDwObjectPersistence::LoadObjectsFromResult($oClass, $oRes)
0724. 
0725. 
0726. 
0727. 
0728.        /**<***********************************************************************>
0729.        *  Saves an object with ID $iID  of the given class $oClass                *
0730.        ***************************************************************************/
0731.        function SaveObject($oObject){
0732.                //echo "<pre>\n class object: ".AdjustedPrintR($oClass)."</pre>";///
0733.                //App::Log("Object: ".AdjustedPrintR($oObject));///
0734.                if( !is_object( $oObject ) ) return false;
0735. 
0736. 
0737.                // Whether to try UPDATE first before INSERT
0738.                $bDoUpdate = true;
0739.                $bZeroUpdatedRows = false;
0740. 
0741. 
0742.                // Add this Class object into the dictionary
0743. 
0744.                // Get the Class object - for this PHP class or some ancestor class
0745.                $sClassName = get_class($oObject);
0746.                do{
0747.                        $oClass = $this->GetClassFromDictionary($sClassName);
0748.                        $sClassName = get_parent_class($sClassName);
0749.                }while( $sClassName && !$oClass );
0750. 
0751.                // Check class object //
0752.                if( !$oClass )
0753.                        return false;
0754. 
0755.                $sTable  = $oClass->GetTable();
0756. 
0757. 
0758. 
0759.                /// --- PRIMARY KEYS --- ///
0760. 
0761. 
0762.                /*/ For one ID (PRIMARY KEY) property only...
0763.                $oIdProp = $oClass->GetIdProperty();         //x  Single-ID way
0764.                $sColId  = $oIdProp->GetColName();           //x  Single-ID way
0765.                $iID = $oObject->GetProperty( $oIdProp->GetColName() );
0766.                $sIdForSql = $oIdProp->FormatForSql($iID); //x  Single-ID way
0767.                /*/
0768.                // For all PRIMARY KEYs:
0769.                $saxIds = $oClass->GetIdPropertiesSql( $oObject );
0770.                $saxIdsCond = $oClass->GetIdPropertiesSql( $oObject, ' AND ' );
0771.                /**/
0772. 
0773. 
0774. 
0775. 
0776. 
0777.                /// --- VALUES --- ///
0778. 
0779. 
0780.                // Store all declared properties - create the SQL part
0781.                $asSqlParts = Array();
0782.                $aoIdProps = $oClass->GetIdProperties();
0783.                foreach( $oClass->GetProperties() as $oProperty ){
0784.                        //echo "<div>\$oObject->GetProperty( ".$oProperty->GetColName()." );</div>";///
0785.                        $bPropertySet = $oObject->HasProperty( $oProperty->GetName() );
0786.                        //App::Log("HasProperty(".$oProperty->GetName().") ? : ".(int)$oObject->HasProperty($oProperty->GetName()) );///
0787. 
0788. 
0789.                        // Check whether PRIMARY KEY properties are set and skip them.
0790.                        //if( $oProperty == $oIdProp ) continue;
0791.                        //if( $oProperty->IsIdProperty() ) continue;
0792.                        if( in_array($oProperty, $aoIdProps) ){
0793.                                // Multi-property PRIMARY key and some is not set -> error
0794.                                if( !$bPropertySet  &&  1 < Count($aoIdProps) ){
0795.                                        $this->SetError("Multi-property PRIMARY - property [".$oProperty->GetName()."] not set! in ".__METHOD__." @ ".__LINE__);
0796.                                        //App::Log("Error!");///
0797.                                        return false;
0798.                                }
0799.                                // Skip ID column(s) - we don't want to set them as other properties
0800.                                //App::Log("Skipping ID col [".$oProperty->GetName()."]");///
0801.                                continue;
0802.                        }
0803. 
0804.                        // Skip undefined properties - or set them to DEFAULT ???  ->  TODO
0805.                        if( !$bPropertySet ){
0806.                                //$xVal = $oProperty->GetDefault();
0807.                                continue;
0808.                        }
0809. 
0810.                        //App::Log("Adding col [".$oProperty->GetName()."]");///
0811.                        $sPropColName = $oProperty->GetColName();
0812.                        $xVal = $oObject->GetProperty( $sPropColName );
0813.                        //if( $sPropColName == 'targeting_fee' ) App::Log("FDG: (".gettype($xVal).") $xVal, ".AdjustedPrintR($oProperty));///
0814.                        if( null === $xVal && !$oProperty->GetColType()->IsNull() )
0815.                                $xVal = $oProperty->GetColType()->GetDefault();
0816.                        $asSqlParts[] = $sPropColName.'='.$oProperty->FormatForSql($xVal);
0817.                }
0818.                $saParts = implode(', ', $asSqlParts);
0819.                //App::Log("\$saParts: ".$saParts);///
0820. 
0821.                // Multiple KEY and nothing set - error (we do not INSERT unset multi-column rows )
0822.                // So other way:  Don't try UPDATE if no other property set.
0823.                if( 0 == Count($asSqlParts) &&  1 < Count($aoIdProps) ){
0824.                        //$this->SetError("Multiple KEY and no normal properties set. in ".__METHOD__." @ ".__LINE__);); return false;
0825.                        $bDoUpdate = false;
0826.                        $bZeroUpdatedRows = true;
0827.                }
0828. 
0829. 
0830. 
0831. 
0832.                // Whether to add the object into the Object Pool.
0833.                // Generally, add if the object was created; don't overwrite we are only saving existing object.
0834.                $bAddObjectIntoPool = true;
0835.                $bOverwriteObjectInPool = false;
0836. 
0837.                // If we are saving single-prop PRIMARY KEYed object and it is set to null, we should inser new.
0838.                //App::Log("ID: (".gettype($oObject->GetId()).") ".$oObject->GetId());///
0839.                if( 1 == $oClass->GetIdPropertiesCount() && (null === $oObject->GetId()) ){
0840.                        $bDoUpdate = false;
0841.                }
0842. 
0843.                // Multi-property PRIMARY key and some is not set -> error
0844.                //if( 1 < $oClass->GetIdPropertiesCount() ){ } // Done above
0845. 
0846.                // UPDATE
0847.                //if( null !== $oObject->GetId() )
0848.                if( $bDoUpdate ){
0849.                        // Create SQL query
0850.                        //$sSQL = "UPDATE $sTable SET %s WHERE $sColId = ".$sIdForSql;  //x  Single-ID way
0851.                        $sSQL = "UPDATE $sTable SET %s WHERE $saxIdsCond";
0852.                        $sSQL = sprintf($sSQL, $saParts );
0853.                        //echo "<div>$sSQL</div>";///
0854. 
0855.                        // Perform SQL query
0856.                        $oRet = $this->GetDB()->Execute($sSQL);
0857.                        //echo $oRet->GetError();///
0858.                        if( !($oRet->IsOK()) ){ $this->SetError($oRet->GetError()); return false; }
0859.                        if( 0 < $oRet->NumRows() ){
0860.                                $this->AddObjectIntoPool( $oObject );
0861.                                return true;
0862.                        }
0863.                        $bZeroUpdatedRows = true;
0864.                        /*// If commented, tries to INSERT. If not, checks here for the row existence first.
0865.                        // No rows affected - did we UPDATE with no changes, or the row was not found?
0866.                        $sSQL = "SELECT COUNT(*) FROM $sTable WHERE $sColId = ".$sIdForSql;
0867.                        //$oRet = $this->GetDB()->Execute($sSQL);
0868.                        //if( !($oRet->IsOK()) ){ $this->SetError($oRet->GetError()); return false; }
0869.                        //if( 0 != $oRet->GetCell(0,0) ){
0870.                        $sVal = $oRet->SelectCell($sSQL, 0,0);
0871.                        if( null === $sVal ){ $this->SetError("Error in SelectCell: [$sSQL]"); return false; }
0872.                        if( 0 != $oRet->GetCell(0,0) ){
0873.                                $this->AddObjectIntoPool( $oObject );
0874.                                return true;
0875.                        }/**/
0876.                }
0877. 
0878.                // Row with ID not found  or  object ID is NULL -> INSERT
0879.                do{
0880.                        //$sSQL = "INSERT INTO $sTable SET $sColId = $sIdForSql, ".$saParts;
0881.                        $sSQL = "INSERT INTO $sTable SET $saxIds ";
0882.                        if( $saParts ) $sSQL .= ", ".$saParts;
0883.                        //echo "<div>$sSQL</div>";///
0884. 
0885. 
0886.                        // Perform SQL query
0887.                        //echo "\nSelectMode: ".(int)$this->GetDB()->GetSelectMode();///
0888.                        $oRet = $this->GetDB()->Execute($sSQL);
0889. 
0890.                        // If we did UPDATE with no changes,
0891.                        if($bZeroUpdatedRows){
0892.                                // And this insert caused error 1062, then it's OK - the row just existed before.
0893.                                if( !$oRet || (!$oRet->IsOK() && $oRet->GetErrno() == 1062 ) ){
0894.                                        break;
0895.                                }
0896.                        }
0897.                        /*/ Other way:
0898.                        // If INSERT caused an error:
0899.                        if( !$oRet || !$oRet->IsOK() ){
0900.                                // If we did not UPDATE with no changes  OR   the error is DUPLICATE ENTRY
0901.                                if(!$bZeroUpdatedRows || ( !$oRet || $oRet->GetErrno() == 1062 ) )
0902.                                        $this->SetError("INSERT failed. ".($oRet ? '['.$oRet->GetError().']' : '')); return false;
0903.                        }/* Too complex */
0904.                        if( 0 == $oRet->NumRows() ){ $this->SetError("INSERT affected no rows. SQL: [".$sSQL."] Errno: ".$oRet->GetErrno()); return false; }
0905.                        $oObject->SetId( $this->GetDB()->GetLastInsertId() );
0906. 
0907.                        // We created a new object, thus if any of the same ID is in the pool, overwrite.
0908.                        $bOverwriteObjectInPool = true;
0909. 
0910.                }while(false);
0911. 
0912.                // If asked, put object into the bool. If already there, it's overwritten.
0913.                if( $bAddObjectIntoPool )
0914.                        $this->AddObjectIntoPool( $oObject, $bOverwriteObjectInPool );
0915.                return true;
0916. 
0917.        }// LoadObjectById($oClass, $iID)
0918. 
0919. 
0920. 
0921. 
0922. 
0923.        /**<***********************************************************************>
0924.        *  Creates a cDwOpClass object copy.                                       *
0925.        ***************************************************************************/
0926.        function CreateClassCopy( $sName, $xClass ){
0927. 
0928.                // The second parameter can be either cDwOpClass object or a class name
0929.                if( is_string($xClass) ){
0930.                        $sClassName = $xClass;
0931.                        $xClass = $this->GetClassFromDictionary($sClassName);
0932.                        if( !is_object($xClass) ){
0933.                                $this->SetError("Class [$sClassName] not found in dictionary.");
0934.                                return false;
0935.                        }
0936.                }else{
0937.                        $sClassName = $xClass->GetName();
0938.                }
0939. 
0940.                // Make a copy
0941.                $oClass = clone $xClass;
0942.                $oClass->SetName( $sName );
0943.                $this->AddClassIntoDictionary($oClass);
0944.                return $oClass;
0945.        }
0946. 
0947. 
0948.        /**<***********************************************************************>
0949.        *  Creates a cDwOpClass using it's definition from database.               *
0950.        ***************************************************************************/
0951.        function CreateClassByTable( $sName, $sTable ){
0952. 
0953.                do{
0954.                        $oClass = new cDwOpClass( $sName, $sTable );
0955. 
0956.                        // Load columns info
0957.                        $sSQL = "SHOW COLUMNS FROM $sTable";
0958.                        $oRes = $this->GetDB()->Select($sSQL);
0959.                        if( !$oRes || !$oRes->IsOK() ){ $this->SetError("SQL error [".$oRes->GetError()."] SQL: [$sSQL]"); return null; }
0960. 
0961.                        // For each row (which represent columns in the table)
0962.                        while( $a = $oRes->FetchRow(MYSQL_ASSOC) ){
0963.                                $oColObject = $this->CreateColumnTypeObject( $a['Type'], $a['Null']=='YES', $a['Default'] );
0964.                                if( null === $oColObject ){
0965.                                        $this->SetError("Table column [$sTable.".$a['Field']."]: ".$this->GetError()." in ".__METHOD__." @ ".__LINE__);
0966.                                        //App::Log($this->GetError());///
0967.                                        $oClass = null; break;
0968.                                }
0969.                                $oProp = $oClass->AddProperty($a['Field'], $oColObject );
0970.                                $oProp->SetNull($a['Null']=='YES');
0971. 
0972.                                if( 'PRI' == $a['Key'] ){ $oClass->AddIdProperty($oProp); }
0973. 
0974.                                $oProp->SetUnique( 'PRI' == $a['Key']  ||  'UNI' == $a['Key'] );
0975.                        }
0976.                        $oRes->FreeResult();
0977. 
0978.                        if($oClass)
0979.                                $this->AddClassIntoDictionary($oClass);
0980. 
0981.                }while(false);
0982.                return $oClass;
0983.        }
0984. 
0985. 
0986.        //$oClass = $oOP->ExtendClassByTable('cAdplazeAdPage_Coupon', 'cAdplazeAdPage', 'ap_adpages_coupons');/**/
0987.        /**<***********************************************************************>
0988.        *  Extends an existings cDwOpClass with columns from a table.              *
0989.        ***************************************************************************/
0990.        function ExtendClassByTable($sClassNameNew, $sClassNameOld, $sTable){
0991. 
0992.                // Old Class object
0993.                $oClassOld = $this->ConvertAndCheckClass($sClassNameOld);
0994.                if(!$oClassOld){ $this->SetError("!ConvertAndCheckClass($sClassNameOld)"); return null; }
0995.                $sClassNameOld = $oClassOld->GetName();
0996.                $aoOldIdProps = $oClassOld->GetIdProperties();
0997. 
0998.                //$oClass = $this->CreateClassCopy($sClassNameNew, $sClassNameOld);
0999.                //$oClass = $this->CreateClassByTable($sClassNameNew, $sTable);
1000.                $oClass = new cDwOpClass( $sClassNameNew, $sTable );
1001.                $oClass->_SetParentClass( $oClassOld );
1002. 
1003.                // Load columns info
1004.                $sSQL = "SHOW COLUMNS FROM $sTable";
1005.                $oRes = $this->GetDB()->Select($sSQL);
1006.                if( !$oRes || !$oRes->IsOK() ){ $this->SetError("SQL error [".$oRes->GetError()."] SQL: [$sSQL]"); return null; }
1007. 
1008.                // --- Add the Properties to the Class --- //
1009. 
1010.                // Temp array to store matched id properties //
1011.                $aoMatchedIdProps = Array();
1012. 
1013.                // For each row (which represent columns in the table)
1014.                while( $a = $oRes->FetchRow(MYSQL_ASSOC) ){
1015.                        $oColObject = $this->CreateColumnTypeObject( $a['Type'], $a['Null']=='YES', $a['Default'] );
1016.                        if( null === $oColObject ){
1017.                                $this->SetError("Table column [$sTable.".$a['Field']."]: ".$this->GetError()." in ".__METHOD__." @ ".__LINE__);
1018.                                $oClass = null; break;
1019.                        }
1020.                        $oProp = $oClass->AddProperty($a['Field'], $oColObject );
1021.                        $oProp->SetNull($a['Null']=='YES');
1022. 
1023.                        // ID property
1024.                        if( 'PRI' == $a['Key'] ){
1025.                                // Check whether the old Class has the same ID Property
1026.                                if( !$oClassOld->HasIdProperty($oProp->GetName()) ){
1027.                                        $this->SetError("Table column [$sTable.".$a['Field']."]: Extended Class [$sClassNameOld] doesn't have the ID column [".$oProp->GetName()."] in ".__METHOD__." @ ".__LINE__);
1028.                                        $oClass = null; break;
1029.                                }
1030.                                $oClass->AddIdProperty($oProp); // To se tam dostane z CreateClassByTable()
1031.                                // Add this old Class' Property to the list of matched ID Properties
1032.                                $oOldClass_IdProp = $oClassOld->GetProperty($oProp->GetName());
1033.                                $aoMatchedIdProps[] = $oOldClass_IdProp;
1034.                        }
1035. 
1036.                        $oProp->SetUnique( 'PRI' == $a['Key']  ||  'UNI' == $a['Key'] );
1037.                }
1038.                $oRes->FreeResult();
1039. 
1040.                // Check whether all ID Properties of old Class were matched
1041.                foreach( $oClassOld->GetIdProperties() as $oOldClass_IdProp){
1042.                        if( !in_array($oOldClass_IdProp, $aoMatchedIdProps) ){
1043.                                $this->SetError("Extended Class [$sClassNameOld] doesn't have the ID column [".$oOldClass_IdProp->GetName()."] in ".__METHOD__." @ ".__LINE__);
1044.                                $oClass = null; break;
1045.                        }
1046.                }
1047. 
1048.                if($oClass) $this->AddClassIntoDictionary($oClass);
1049.                return $oClass;
1050. 
1051.        }// cDwObjectPersistence::ExtendClassByTable()
1052. 
1053. 
1054. 
1055.        /**<**********************************************************************************>
1056.        *  Returns cDwOpColumnType object according to the definition in $sTypeString.        *
1057.        **************************************************************************************/
1058.        function CreateColumnTypeObject($sTypeString, $bNull, $sDefault){
1059.                $oRet = null;
1060. 
1061.                //$sTypeName = substr( $sTypeString, 0, strpos($sTypeString, '(') );
1062.                //if( !ereg( '(.*)(\\((.*)\\))?(.*)?', $sTypeString, $asParts ) ) return $oRet;
1063.                //$asParts = array_map('strtoupper', array_map('trim', $asParts) );
1064.                //echo "<pre>\$asParts: ".AdjustedPrintR($asParts)."</pre>";///
1065.                // list($sTypeName, $sData, $sUnsigned) = $asParts;
1066.                $sTypeName = strtoupper(trim(strtok($sTypeString, '(') ));
1067.                $sData     =           (trim(strtok(')') ));
1068.                $sUnsigned = strtoupper(trim(strtok('')  ));
1069.                //echo "<pre>$sTypeName, $sData, $sUnsigned</pre>";///
1070. 
1071.                $sClassName = 'cDwOpColumnType_';
1072. 
1073.                /*switch($sTypeName){
1074.                        case 'VARCHAR':
1075.                        case '': $sClassName = '_'.$sTypeName;  break;
1076.                }/**/
1077. 
1078.                $saImplementedColumnTypes = DWOP_IMPLEMENTED_COLUMN_TYPES;
1079.                $asImplementedTypes = array_map('trim', explode(',', $saImplementedColumnTypes));
1080.                if( !in_array( $sTypeName, $asImplementedTypes) ){ $this->SetError("Column type [$sTypeName] not implemented."); return null; }
1081. 
1082.                $sClassName .= $sTypeName;
1083. 
1084.                if( $sUnsigned == 'UNSIGNED' )
1085.                        $sClassName .= '_UNSIGNED';
1086. 
1087.                if(!class_exists($sClassName)){ $this->SetError("Undefined column class [$sClassName]."); return null; }
1088. 
1089.                switch($sTypeName){
1090.                        case 'ENUM': case 'SET':
1091.                                $oRet = new $sClassName($sData); break;
1092.                        case 'CHAR': case 'VARCHAR':
1093.                                $oRet = new $sClassName((int)$sData); break;
1094.                        default:
1095.                                $oRet = new $sClassName(); break;
1096.                }
1097.                if( $oRet ){
1098.                        $oRet->SetDefault($sDefault);
1099.                        $oRet->SetNull($bNull);
1100.                }
1101. 
1102. 
1103.                return $oRet;
1104. 
1105.        }// cDwObjectPersistence::CreateColumnTypeObject()
1106. 
1107. 
1108.        /**<***********************************************************************>
1109.        *   Sets M : N relation between two classes using third class as a glue.   *
1110.        ***************************************************************************/
1111.        function SetMnRelation($oClass1, $oClassGlue, $oClass2){
1112.                $bSucc = false;
1113.                do{
1114. 
1115.                        // Convert class names to Class objects and check the existence of the class.
1116.                        if( !($oClass1 = $this->ConvertAndCheckClass($oClass1)) ){ $this->SetError("!oClass1 in ".__METHOD__." @ ".__LINE__); break; }
1117.                        if( !($oClass2 = $this->ConvertAndCheckClass($oClass2)) ){ $this->SetError("!oClass2 in ".__METHOD__." @ ".__LINE__); break; }
1118.                        if( !($oClassGlue = $this->ConvertAndCheckClass($oClassGlue)) ){ $this->SetError("!oClassGlue in ".__METHOD__." @ ".__LINE__); break; }
1119. 
1120.                        // Check all classes whether suitable (PRIMARY KEYs etc.)
1121.                        if( !$oClass1->IsSuitableForMnRelationSide() ){ $this->SetError("Class ".$oClass1->GetName()." not suitable for M:N side in ".__METHOD__." @ ".__LINE__); break; }
1122.                        if( !$oClass2->IsSuitableForMnRelationSide() ){ $this->SetError("Class ".$oClass2->GetName()." not suitable for M:N side in ".__METHOD__." @ ".__LINE__); break; }
1123.                        if( !$oClassGlue->IsSuitableForMnRelationGlue() ){ $this->SetError("Class ".$oClassGlue->GetName()." not suitable for M:N glue in ".__METHOD__." @ ".__LINE__); break; }
1124. 
1125.                        // Check whether the types are the same
1126.                        $oIdProp1 = $oClass1->GetIdProperty();
1127.                        $oIdProp2 = $oClass1->GetIdProperty();
1128.                        $aoIdProps = $oClassGlue->GetIdProperties();
1129. 
1130.                        if( $oIdProp1->GetColType()->GetType() != $aoIdProps[0]->GetColType()->GetType() ){
1131.                                $this->SetError('Property types mismatch: "'.$oIdProp1->GetName().'" and "'.$aoIdProps[0]->GetName().' in '.__METHOD__.' @ '.__LINE__); break; }
1132.                        if( $oIdProp2->GetColType()->GetType() != $aoIdProps[1]->GetColType()->GetType() ){
1133.                                $this->SetError('Property types mismatch: "'.$oIdProp2->GetName().'" and "'.$aoIdProps[1]->GetName().' in '.__METHOD__.' @ '.__LINE__); break; }
1134. 
1135.                        //$oIdProp1->CompareTo($aoIdProps[0]); // TODO
1136. 
1137.                        $b = $oClassGlue->SetMnRelationGlue($oClass1, $oClass2, $aoIdProps[0], $aoIdProps[1]);
1138.                        if( !$b ){ $this->SetError("!\$oClassGlue->SetMnRelationGlue @ ".__LINE__); break; }
1139. 
1140.                        $bSucc = true;
1141.                }while(false);
1142.                return $bSucc;
1143.        }
1144. 
1145. 
1146.        /**<*********************************************************************************************>
1147.  *                                                                                                *
1148.                @param $oObject1 is an object of which related objects of $oClass2 should be returned.
1149.                @param $oClass2 - can be:
1150.                        (object)     - the Class will be found by it's class name.
1151.                        (cDwOpClass) Class object - that's what we need
1152.                        (string)     class name   - the Class will be found in the dictionary.
1153.  *************************************************************************************************/
1154.  function LoadObjectsByMnRelation($oObject, $oClassGlue, $oClass2, $xId=null){
1155.        $bSucc = false;
1156.        do{
1157. 
1158.                        // Convert class names to Class objects and check the existence of the class.
1159.                        if( !($oClass1 = $this->ConvertAndCheckClass($oObject)) ){ $this->SetError("!oClass1 @ ".__LINE__); break; }
1160.                        if( !($oClass2 = $this->ConvertAndCheckClass($oClass2)) ){ $this->SetError("!oClass2 @ ".__LINE__); break; }
1161.                        if( !($oClassGlue = $this->ConvertAndCheckClass($oClassGlue)) ){ $this->SetError("!oClassGlue @ ".__LINE__); break; }
1162. 
1163.                        if( !$oClassGlue->IsMnRelationGlue() ){ $this->SetError($oClassGlue->GetName()." is not a M : N relation glue in ".__METHOD__." @ ".__LINE__); break; }
1164. 
1165.                        // Table names
1166.                        $sTable1 = $oClass1->GetTable();
1167.                        $sTable2 = $oClass2->GetTable();
1168.                        $sTableGlue = $oClassGlue->GetTable();
1169. 
1170.                        // Column names
1171.                        $oClass1IdProp = $oClass1->GetIdProperty();
1172.                        $sIdCol1 = $oClass1IdProp->GetColName();
1173. 
1174.                        $sIdCol2 = $oClass2->GetIdProperty()->GetColName();
1175.                        $sGluePropFrom = $oClassGlue->oMnRelationProp1->GetColName();
1176.                        $sGluePropTo   = $oClassGlue->oMnRelationProp2->GetColName();
1177. 
1178.                        if( is_object($oObject) )
1179.                                $xId = $oClass1IdProp->FormatForSql( $oObject->GetId() );
1180. 
1181.                        //if( !$xId ){ $this->SetError("Bad ID [$xId] in ".__METHOD__." @ ".__LINE__); break; }
1182. 
1183. 
1184.                        // Create SQL
1185.                        $sSQL = "
1186.                        -- Vypise reklamy a k nim kanaly, ktere jsou s reklamou spojeny.
1187.                        SELECT table_to.*
1188.                        FROM $sTable1 AS table_from
1189.                        INNER JOIN $sTableGlue AS glue ON table_from.$sIdCol1 = glue.$sGluePropFrom
1190.                        LEFT JOIN $sTable2 AS table_to ON glue.$sGluePropTo = table_to.$sIdCol2
1191.                        WHERE table_from.$sIdCol1 = $xId
1192.                        ;";
1193. 
1194.                        $aoObjects = $this->LoadObjectsBySql($oClass2, $sSQL);
1195. 
1196.                        $bSucc = true;
1197.        }while(false);
1198. 
1199.        return $aoObjects;
1200.        }
1201. 
1202. 
1203. 
1204. 
1205.}// cDwObjectPersistence
1206. 
1207. 
1208. 
1209. 
1210./**<***********************************************************************>
1211.*  cDwOpClass                                                              *
1212.***************************************************************************/
1213.class cDwOpClass {
1214. 
1215.        var $sName;
1216.        function GetName()      { return $this->sName; }
1217.        function SetName($sName){ $this->sName = $sName; }
1218. 
1219.        var $sTable;
1220.        function GetTable()       { return $this->sTable; }
1221.        function SetTable($sTable){ $this->sTable = $sTable; }
1222. 
1223.        var $oParentClass = null;
1224.        function GetParentClass()             { return $this->oParentClass; }
1225.        function _SetParentClass($oParentClass){ $this->oParentClass = $oParentClass; }
1226. 
1227. 
1228. 
1229.        // -- Constructor -- //
1230.        function cDwOpClass( $sName, $sTable ){ $this->sName = $sName; $this->SetTable($sTable); }
1231. 
1232.        // Properties //
1233.        var $aoProperties;
1234.        /** @returns cDwOpProperty[] array of this class' properties. */
1235.        function GetProperties()             { return $this->aoProperties; }
1236.        /** @returns cDwOpProperty this class' property of given name or NULL if the class does not have such. */
1237.        function GetProperty($sColName){
1238.                if( isset($this->aoProperties[$sColName]) )
1239.                        $oRet = $this->aoProperties[$sColName];
1240.                else $oRet = null;
1241.                return $oRet;
1242.        }
1243.        function AddPropertyObject($oProperty){ return $this->aoProperties[$oProperty->GetColName()] = $oProperty; }
1244.        function AddProperty($sColName, $oColType){
1245.                $oProp = new cDwOpProperty($sColName, $oColType);
1246.                $this->aoProperties[$sColName] = $oProp;
1247.                return $oProp;
1248.        }
1249.        function RemProperty($sColName){
1250.                if( !isset($this->aoProperties[$sColName]) ) return false;
1251.                unset($this->aoProperties[$sColName]);
1252.                return true;
1253.        }
1254.        function GetNonKeyProperties(){
1255.                $aoProps = Array();
1256.                foreach( $this->aoProperties as $oProp ){
1257.                        //if( $oProp->IsKeyProp() ) continue;
1258.                        $aoProps[] = $oProp;
1259.                }
1260.                return $aoProps;
1261.        }
1262. 
1263. 
1264. 
1265.        // --- M : N relation stuff --- //
1266. 
1267. 
1268.        // -- Is this class M : N relation "glue"? -- //
1269.        var $bMnRelationGlue = false;
1270.        function IsMnRelationGlue()            { return $this->bMnRelationGlue; }
1271.        function UnsetMnRelationGlue(){ $this->bMnRelationGlue = false; }
1272. 
1273.        var $oMnRelationClass1 = null;
1274.        var $oMnRelationClass2 = null;
1275.        function GetMnRelationClass1()                  { return $this->oMnRelationClass1; }
1276.        function GetMnRelationClass2()                  { return $this->oMnRelationClass2; }
1277.        function GetMnRelationOtherClass($oClass){
1278.                if( $oClass === $this->oMnRelationClass1 ) return $this->oMnRelationClass2;
1279.                if( $oClass === $this->oMnRelationClass2 ) return $this->oMnRelationClass1;
1280.                return null;
1281.        }
1282. 
1283.        var $oMnRelationProp1 = null;
1284.        var $oMnRelationProp2 = null;
1285.        function GetMnRelationProp1()                 { return $this->oMnRelationProp1; }
1286.        function GetMnRelationProp2()                 { return $this->oMnRelationProp2; }
1287.        function GetMnRelationOtherProp($oProp){
1288.                if( $oProp === $this->oMnRelationProp1 ) return $this->oMnRelationProp2;
1289.                if( $oProp === $this->oMnRelationProp2 ) return $this->oMnRelationProp1;
1290.                return null;
1291.        }
1292.        function GetMnRelationPropForClass($oClass){
1293.                //App::Log($oClass->GetName()." ==? ".$this->oMnRelationClass1->GetName());///
1294.                //App::Log($oClass->GetName()." ==? ".$this->oMnRelationClass2->GetName());///
1295.                //if( $oClass === $this->oMnRelationClass1 ) return $this->oMnRelationProp1;
1296.                //if( $oClass === $this->oMnRelationClass2 ) return $this->oMnRelationProp2;
1297.                $asParents = class_parents($oClass->GetName());
1298.                array_unshift($asParents, $oClass->GetName());
1299.                //App::Log(implode(',',$asParents)." <==? ".$this->oMnRelationClass1->GetName());///
1300.                //App::Log(implode(',',$asParents)." <==? ".$this->oMnRelationClass2->GetName());///
1301.                if( in_array($this->oMnRelationClass1->GetName(), $asParents)){ return $this->oMnRelationProp1; }
1302.                if( in_array($this->oMnRelationClass2->GetName(), $asParents)){ return $this->oMnRelationProp2; }
1303.                return null;
1304.        }
1305. 
1306. 
1307. 
1308. 
1309.        function IsSuitableForMnRelationGlue(){ return Count($this->aoIdProperties) == 2; }
1310.        function IsSuitableForMnRelationSide(){ return Count($this->aoIdProperties) == 1; }
1311. 
1312.        function SetMnRelationGlue($oClass1, $oClass2, $oProp1, $oProp2){
1313.                $this->bMnRelationGlue = false;
1314.                do{
1315.                        // Check everything and return false on failure.
1316.                        if( !$this->IsSuitableForMnRelationGlue() ) return false;
1317.                        if( !$oClass1 || !($oClass1 instanceof cDwOpClass) ) return false;
1318.                        if( !$oClass2 || !($oClass2 instanceof cDwOpClass) ) return false;
1319. 
1320.                        if( 1 != $oClass1->GetIdPropertiesCount() ) return false;
1321.                        if( 1 != $oClass2->GetIdPropertiesCount() ) return false;
1322. 
1323.                        if( is_string($oProp1) ) $oProp1 = $this->GetProperty($oProp1);
1324.                        if( is_string($oProp2) ) $oProp2 = $this->GetProperty($oProp2);
1325. 
1326.                        if( null == $oProp1  ||  !($oProp1 instanceof cDwOpProperty) ) return false;
1327.                        if( null == $oProp2  ||  !($oProp2 instanceof cDwOpProperty) ) return false;
1328. 
1329.                        // Seems to be ok, set the relation for this Class... (we have to set the sides, too)
1330.                        $this->oMnRelationClass1 = $oClass1;
1331.                        $this->oMnRelationClass2 = $oClass2;
1332.                        $this->oMnRelationProp1 = $oProp1;
1333.                        $this->oMnRelationProp2 = $oProp2;
1334. 
1335.                        $this->bMnRelationGlue = true;
1336. 
1337.                }while(false);
1338.                return $this->bMnRelationGlue;
1339.        }
1340. 
1341. 
1342. 
1343.        // --- ID properties --- //
1344. 
1345.        // ID property //
1346.        //var $oIdProperty;
1347.        function GetIdProperty()            { reset($this->aoIdProperties); return current($this->aoIdProperties); }
1348.        function SetIdProperty($oIdProperty){
1349.                if(!$oIdProperty){ $this->aoIdProperties = Array(); return; }
1350.                $this->aoIdProperties = Array( $oIdProperty );
1351.                $oIdProperty->SetPrimaryKeyPart(true);
1352.        }
1353. 
1354.        var $aoIdProperties = Array();
1355.        function GetIdProperties()            { return $this->aoIdProperties; }
1356. 
1357.        function GetIdPropertiesCount()       { return Count($this->aoIdProperties); }
1358. 
1359. 
1360.        /**<**********************************************************************************>
1361.        *  @returns whether Class has given ID property  or ID property of given name         *
1362.        **************************************************************************************/
1363.        function HasIdProperty($xProp){
1364.                if( is_string($xProp) ) $xProp = $this->GetProperty($xProp);
1365.                if( !$xProp ) return false;
1366.                if( in_array($xProp, $this->aoIdProperties) ) return true;
1367.                return false;
1368.        }
1369. 
1370.        function AddIdProperty($oProperty){ array_push($this->aoIdProperties, $oProperty); $oProperty->SetPrimaryKeyPart(true); }
1371. 
1372.        /**  @returns string Coma separated list of ID (PRIMARY KEY) values of the given object. */
1373.        function GetIdPropertiesSql($oObject, $sGlue=', '){
1374.                $asPropSqls = Array();
1375.                foreach( $this->GetIdProperties() as $oProp ){
1376.                        $asPropSqls[] = $oProp->GetColName() . " = " . $oProp->FormatForSql( $oObject->GetProperty($oProp->GetName()) );
1377.                }
1378.                $saPropSqls = implode($sGlue, $asPropSqls);
1379.                //if( '' != $saPropSqls ) $saPropSqls = " WHERE $saPropSqls "; // We don't want this for ID - can be "ON ...", e.g.
1380.                return $saPropSqls;
1381.        }
1382. 
1383.        /**  @returns string Formatted list of ID (PRIMARY KEY) values of the given object. */
1384.        function GetObjectMultiIdString($oObject, $iMode=DWOP_VALUE_DUMP_MODE__SQL){
1385.                switch($iMode){
1386.                        default: trigger_error("Bad param 2 - must be one of DWOP_VALUE_DUMP_MODE__* constants"); return null; break;
1387.                        case DWOP_VALUE_DUMP_MODE__SQL:            $sGlue=', '; $sEq=' = '; $fFunc='sql'; break;
1388.                        case DWOP_VALUE_DUMP_MODE__SQL_SHORT:      $sGlue=','$sEq='=';   $fFunc='sql'; break;
1389.                        case DWOP_VALUE_DUMP_MODE__SQL_VALUES:     $sGlue=','$sEq='';    $fFunc='sql'; break;
1390.                        case DWOP_VALUE_DUMP_MODE__ESCAPED_VALUES: $sGlue='\''; $sEq='';    $fFunc='addslashes'; break; // Shortest unique
1391.                        case DWOP_VALUE_DUMP_MODE__COMA:           $sGlue=', '; $sEq='';    $fFunc=''; break;
1392.                }
1393.                $asPropSqls = Array();
1394.                foreach( $this->GetIdProperties() as $oProp ){
1395.                        $s = '';
1396.                        if($sEq) $s .= $oProp->GetColName() . $sEq; // <col name> =
1397. 
1398.                        $xVal = $oObject->GetProperty($oProp->GetName());
1399.                        if(!$fFunc) ;
1400.                        elseif( 'sql' == $fFunc $xVal = $oProp->FormatForSql( $xVal );
1401.                        else/*if( function_exists($fFunc) ) */$xVal = $fFunc($xVal);
1402.                        //else ;
1403.                        $s .= $xVal;
1404. 
1405.                        $asPropSqls[] = $s;
1406.                }
1407.                $saPropSqls = implode($sGlue, $asPropSqls);
1408.                //if( '' != $saPropSqls ) $saPropSqls = " WHERE $saPropSqls "; // We don't want this for ID - can be "ON ...", e.g.
1409.                return $saPropSqls;
1410.        }
1411. 
1412. 
1413.        /**  Sets ID properties.
1414.             @param $xProperties can be:
1415.                                        - a Property name
1416.                            - coma separated list of Property names
1417.                                        - array of Property objects
1418.        */
1419.        function SetIdProperties($xProperties){
1420. 
1421.                // If $xProperties param is string, convert to an array
1422.                if( is_string($xProperties) )
1423.                        $xProperties = array_map('trim', explode(',', $xProperties));
1424. 
1425.                // Now the $xProperties must be an array.
1426.                if( !is_array($xProperties) ){
1427.                        $this->aoIdProperties = Array();
1428.                        return false;
1429.                }
1430. 
1431. 
1432. 
1433.                // Go through all properties in the array and transform them to cDwOpIdingInfo objects.
1434.                $aoIdProperties = Array();
1435.                foreach( $xProperties as $xProperty ){
1436.                        $bDesc = false;
1437.                        // If the array item is a string, convert to a property object.
1438.                        if( is_string($xProperty) )
1439.                                $xProperty = $this->GetProperty($xProperty);
1440. 
1441.                        // If the $xProperty is not a cDwOpProperty object, move on.
1442.                        if( null === $xProperty || !($xProperty instanceof cDwOpProperty) )
1443.                                continue;
1444. 
1445.                        $xProperty->SetPrimaryKeyPart(true);
1446.                        $aoIdProperties[] = $xProperty;
1447.                }
1448. 
1449.                $this->aoIdProperties = $aoIdProperties;
1450.                //echo "<pre>".AdjustedPrintR($aoOrderProperties)."</pre>";///
1451. 
1452.        }// SetIdProperties($xProperties)
1453. 
1454. 
1455. 
1456. 
1457.        // --- Order properties --- //
1458.        var $aoOrderProperties = Array();
1459.        function GetOrderProperties()                  { return $this->aoOrderProperties; }
1460. 
1461. 
1462.        /**  @returns string Coma separated list of Order Properties (column names). */
1463.        function GetOrderPropertiesSql(){
1464.                $asPropSqls = Array();
1465.                foreach( $this->aoOrderProperties as $oOrderingInfo ){
1466.                        $asPropSqls[] = $oOrderingInfo->oProperty->GetColName() . ($oOrderingInfo->bDesc ? ' DESC' : ' ASC');
1467.                }
1468.                $saPropSqls = implode(', ', $asPropSqls);
1469.                if( '' != $saPropSqls ) $saPropSqls = " ORDER BY $saPropSqls ";
1470.                return $saPropSqls;
1471.        }
1472. 
1473. 
1474.        /**<*************************************************************************>
1475.        *  Sets the ordering information for this class. Previous info is replaced.  *
1476.           @param $xProperties can be a coma separated list of Property names,
1477.                                            an array of Property names, or
1478.                                            an array of Property objects.
1479.        *****************************************************************************/
1480.        function SetOrderProperties($xProperties){
1481.                // If $xProperties param is string, convert to an array
1482.                if( is_string($xProperties) )
1483.                        $xProperties = array_map('trim', explode(',', $xProperties));
1484. 
1485.                // Now the $xProperties must be an array.
1486.                if( !is_array($xProperties) )
1487.                        return false;
1488. 
1489.                // Go through all properties in the array and transform them to cDwOpOrderingInfo objects.
1490.                $aoOrderProperties = Array();
1491.                foreach( $xProperties as $xProperty ){
1492.                        $bDesc = false;
1493.                        // If the array item is a string, convert to a property object.
1494.                        if( is_string($xProperty) ){
1495.                                if( '' == $xProperty ) continue;
1496.                                if( $xProperty[0] == '+' || $xProperty[0] == '-' ){
1497.                                        $bDesc = ( $xProperty[0] == '-' );
1498.                                        $xProperty = substr($xProperty, 1);
1499.                                }
1500.                                $xProperty = $this->GetProperty($xProperty);
1501.                        }
1502. 
1503.                        // Instance of cDwOpProperty - convert to cDwOpOrderingInfo
1504.                        if( $xProperty instanceof cDwOpProperty ){
1505.                                $xProperty = new cDwOpOrderingInfo($xProperty, $bDesc);
1506.                        }
1507. 
1508.                        // If the $xProperty is not a cDwOpProperty object, move on.
1509.                        if( null === $xProperty || !($xProperty instanceof cDwOpOrderingInfo) )
1510.                                continue;
1511. 
1512.                        $aoOrderProperties[] = $xProperty;
1513.                }
1514. 
1515.                $this->aoOrderProperties = $aoOrderProperties;
1516.                //echo "<pre>".AdjustedPrintR($aoOrderProperties)."</pre>";///
1517. 
1518.        }// SetOrderProperties($xProperties)
1519. 
1520. 
1521.}// class cDwOpClass
1522. 
1523. 
1524. 
1525. 
1526. 
1527. 
1528./**<***********************************************************************>
1529.*  Holds information about ordering property - for cDwOpClass.             *
1530.***************************************************************************/
1531.class cDwOpOrderingInfo {
1532.        var $oProperty = null;
1533.        var $bDesc = false;
1534.        function cDwOpOrderingInfo($oProperty, $bDesc=false){
1535.                $this->oProperty = $oProperty;
1536.                $this->bDesc = $bDesc;
1537.        }
1538.}
1539. 
1540. 
1541. 
1542. 
1543./**<***********************************************************************>
1544.*  cDwOpProperty                                                           *
1545.***************************************************************************/
1546.class cDwOpProperty {
1547. 
1548.        /**  Name - currently we use the same as a Property name and database column name.  */
1549.        //var $sName = null;
1550.        function GetName()         { return $this->sColName; }
1551.        function SetName($sColName){ $this->sColName = $sColName; }
1552. 
1553.        /**  Column name - currently used also as the name of the property  */
1554.        var $sColName = null;
1555.        function GetColName()         { return $this->sColName; }
1556.        function SetColName($sColName){ $this->sColName = $sColName; }
1557. 
1558.        /**  Column type - an object derived from cDwOpColumnType class.  */
1559.        var $oColType = null;
1560.        /** @returns cDwOpColumnType column type representing this property. */
1561.        function GetColType()         { return $this->oColType; }
1562.        function SetColType($oColType){ $this->oColType = $oColType; }
1563. 
1564.        /**  Whether the property is a part of a PRIMARY KEY for this Class.  */
1565.        var $bPrimaryKeyPart = false;
1566.        function IsPrimaryKeyPart()                { return $this->bPrimaryKeyPart; }
1567.        function SetPrimaryKeyPart($bPrimaryKeyPart){ $this->bPrimaryKeyPart = $bPrimaryKeyPart; }
1568. 
1569.        /**  Whether the property is a FOREIGN KEY.  */
1570.        var $bForeignKey = false;
1571.        function IsForeignKey()            { return $this->bForeignKey; }
1572.        function SetForeignKey($bForeignKey){ $this->bForeignKey = $bForeignKey; }
1573. 
1574.        /**  Whether the property is unique across all objects of this Class.  */
1575.        var $bUnique = false;
1576.        function IsUnique()         { return $this->bUnique; }
1577.        function SetUnique($bUnique){ $this->bUnique = $bUnique; }
1578. 
1579.        /**  Whether this property can be NULL.  */
1580.        var $bCanBeNull = false;
1581.        function IsNull()            { return $this->bCanBeNull; }
1582.        function SetNull($bCanBeNull){ $this->bCanBeNull = $bCanBeNull; }
1583. 
1584.        function IsKey(){
1585.                return $this->IsPrimaryKeyPart() || $this->IsForeignKey() || $this->IsUnique();
1586.        }
1587. 
1588. 
1589.        // -- Constructor -- //
1590.        function cDwOpProperty($sColName, $oColType, $bUnique=false){
1591.                $this->SetColName($sColName);
1592.                $this->SetColType($oColType);
1593.                $this->SetUnique($bUnique);
1594.        }
1595. 
1596.        /**  @returns SQL literal representing a value $xVal if it was held by this property. */
1597.        function FormatForSql($xVal){
1598.                if( null === $xVal  &&  $this->GetColType()->IsNull() )
1599.                        return 'NULL';
1600.                //echo "\n".CallInfo()."; Val: ".$xVal." ColType:".$this->GetColType()->GetName();///
1601.                return $this->GetColType()->FormatForSql($xVal);
1602.        }
1603. 
1604.}// class cDwOpProperty
1605. 
1606. 
1607. 
1608. 
1609. 
1610. 
1611. 
1612. 
1613./**<***********************************************************************>
1614.*  cDwOpColumnType                                                         *
1615.***************************************************************************/
1616.abstract class cDwOpColumnType {
1617. 
1618.        var $sName;
1619.        function GetName()      { return $this->sName; }
1620.        function SetName($sName){ $this->sName = $sName; }
1621. 
1622.        var $iType;
1623.        /** @returns  one of type constants: DWOP_TYPE_STR,DWOP_TYPE_NUM,DWOP_TYPE_BOOL,DWOP_TYPE_ENUM,DWOP_TYPE_SET, DWOP_TYPE_REAL */
1624.        function GetType()      { return $this->iType; }
1625.        /** @param iType  one of type constants: DWOP_TYPE_STR,DWOP_TYPE_NUM,DWOP_TYPE_BOOL,DWOP_TYPE_ENUM,DWOP_TYPE_SET, DWOP_TYPE_REAL */
1626.        function SetType($iType){
1627.                if(!in_array($iType, Array(DWOP_TYPE_STR,DWOP_TYPE_NUM,DWOP_TYPE_BOOL,DWOP_TYPE_ENUM,DWOP_TYPE_SET, DWOP_TYPE_REAL))){
1628.                        user_error("cDwOpColumnType::SetType(): Param \$iType must be some of DWOP_TYPE_* constants.");
1629.                }
1630.                $this->iType = $iType;
1631.        }
1632. 
1633.        // Can be NULL?
1634.        var $bCanBeNull = true;
1635.        function IsNull()            { return $this->bCanBeNull; }
1636.        function SetNull($bCanBeNull){ $this->bCanBeNull = $bCanBeNull; }
1637. 
1638.        // Default value
1639.        var $xDefault;
1640.        function GetDefault()         { return $this->xDefault; }
1641.        function SetDefault($xDefault){
1642.                $bRet = true;
1643.                if( (null === $xDefault && !$this->IsNull() )  // Default value being set is NULL and must not be null
1644.                 || ( !$this->CheckValue($xDefault) )          // or  the default value is not allowed for this type,
1645.                ){
1646.                        $xDefault = $this->GetDefaultDefault();      // then set the default to the default default,
1647.                        $bRet = false;                               // and indicate failure by returning false.
1648.                }
1649.                $this->xDefault = $xDefault;
1650.                return $bRet;
1651.        }
1652.        //function GetDefaultDefault(){ user_error(__METHOD__.' must be overriden.'); return null; }
1653.        function GetDefaultDefault(){
1654.                switch( $this->GetType() ){
1655.                        case DWOP_TYPE_STR:  return ''break;
1656.                        case DWOP_TYPE_NUM:  return 0;   break;
1657.                        case DWOP_TYPE_BOOL: return false; break;
1658.                        case DWOP_TYPE_ENUM: return ''break;
1659.                        case DWOP_TYPE_SET:  return ''break;
1660.                        case DWOP_TYPE_REAL: return 0.0; break;
1661.                }
1662.        }
1663. 
1664. 
1665.        // Aditional data - possible values for ENUM and SET, etc
1666.        //var $aData;
1667. 
1668. 
1669. 
1670.        /**<***********************************************************************>
1671.        *  Constructor                                                             *
1672.        ***************************************************************************/
1673.        function cDwOpColumnType($iFlags=0, $xDefault=null){
1674. 
1675.                // Flags
1676.                if( $iFlags & DWOP_NOT_NULL )
1677.                        $this->SetNull(false);
1678.                //if( $iFlags & DWOP_BINARY )
1679.                //      $this->SetBinary(true);
1680. 
1681.                // Default value
1682.                $this->SetDefault( $xDefault );
1683. 
1684.        }// cDwOpColumnType::cDwOpColumnType()
1685. 
1686. 
1687.        // Useless
1688.        /*function cDwOpColumnType($sName, $iType, $aData){
1689.                $this->SetName($sName);
1690.                $this->SetType($iType);
1691.                $this->aData = $aData;
1692.        }// cDwOpColumnType::cDwOpColumnType()
1693.        /**/
1694. 
1695. 
1696.        /**<***********************************************************************>
1697.        *  @returns the value in $xVal in the format suitable for SQL.             *
1698.        ***************************************************************************/
1699.        function FormatForSql($xVal){
1700.                //App::Log( CallInfo()."     \$xVal: (".gettype($xVal).") $xVal" );
1701.                $sRet = null;
1702. 
1703.                if( null === $xVal ){
1704.                        if( $this->IsNull() )
1705.                                $sRet = 'NULL';
1706.                        else
1707.                                $sRet = 'nULl';
1708.                }
1709.                else switch($this->GetType()){
1710.                        case DWOP_TYPE_STR:  $sRet = asq((string)$xVal); break;
1711.                        case DWOP_TYPE_NUM:  $sRet = (double)$xVal; /*echo "<br>XXX".CallInfo(-2)."<br>".CallInfo(-3);*/ break;
1712.                        case DWOP_TYPE_BOOL: $sRet = (integer)(boolean)$xRet; break;
1713.                        case DWOP_TYPE_ENUM: $sRet = asq((string)$xVal); break;
1714.                        case DWOP_TYPE_SET:  $sRet = asq((string)$xVal); break;
1715.                        case DWOP_TYPE_REAL: $sRet = (double)$xVal; break;
1716.                        //case DWOP_TYPE_:  $sRet = ; break;
1717.                        default: trigger_error('Unknown value type! in '.__METHOD__.' @ '.__LINE__); $sRet = asq((string)$xVal); break;
1718.                }
1719.                //App::Log( CallInfo()."     \$sRet: $sRet" );
1720.                return $sRet;
1721.        }
1722. 
1723.        function ConvertFromResult($xVal){ return $xVal; }
1724. 
1725. 
1726.        /**<***********************************************************************>
1727.        *  Check whether the variable can fit in the column.                       *
1728.        ***************************************************************************/
1729.        function CheckValue($xVal /*, $oProperty*/){
1730. 
1731.                if( null === $xVal && !$this->IsNull() )
1732.                        return false;
1733. 
1734.                $bFits = true;
1735.                switch($this->GetType()){
1736.                        case DWOP_TYPE_STR:  $bFits = is_string($xVal) || is_numeric($xVal); break;
1737.                        case DWOP_TYPE_NUM:  $bFits = is_bool($xVal) || is_int($xVal); break;
1738.                        case DWOP_TYPE_BOOL: $bFits = is_bool($xRet) || is_int($xVal); break;
1739.                        //case DWOP_TYPE_ENUM: $bFits = is_string($xVal) /*&& in_array($xVal, $this->asMembers)/**/; break;
1740.                        case DWOP_TYPE_SET:
1741.                                $asMembers = array_map('trim', explode(',', $xVal));
1742.                                foreach( $asMembers as $sMember ){
1743.                                        if( !in_array($xVal, $this->aData) ){ $bFits = false; break; }
1744.                                }
1745.                        break;
1746.                }
1747.                return $bFits;
1748.        }
1749. 
1750.}// class cDwOpColumnType
1751. 
1752. 
1753. 
1754. 
1755. 
1756. 
1757./**<***********************************************************************>
1758.*  cDwOpColumnTypesRepository                                              *
1759.   Skladiste casto pouzivanych typu sloupcu.
1760.         Nema cenu sem davat stringy - lisi se delkou.
1761.         Mozna je to uplne zbytecna trida...
1762.***************************************************************************/
1763./*class cDwOpColumnTypesRepository {
1764.        var $aoColumnTypes;
1765.        function AddColumnType($sKey, &$oType){ $this->aoColumnTypes[$sKey] =& $oType; }
1766. 
1767. 
1768.        function cDwOpColumnTypesRepository(){
1769.                $this->aoColumnTypes = Array();
1770. 
1771.                $oType =& new cDwOpColumnType_INT();
1772.                $this->AddColumnType('INT', $oType);
1773.                $oType =& new cDwOpColumnType_INT_UNSIGNED();
1774.                $this->AddColumnType('INT UNSIGNED', $oType);
1775.                $oType =& new cDwOpColumnType_SMALLINT();
1776.                $this->AddColumnType('SMALLINT', $oType);
1777.                $oType =& new cDwOpColumnType_SMALLINT_UNSIGNED();
1778.                $this->AddColumnType('SMALLINT UNSIGNED', $oType);
1779. 
1780. 
1781.        }// cDwOpColumnTypesRepository()
1782. 
1783.        function &GetColumnType($sName){
1784.                return isset( $this->aoColumnTypes[$sName] ) ? $this->aoColumnTypes[$sName] : null;
1785.        }
1786. 
1787.}// class cDwOpColumnTypesRepository
1788./**/
1789. 
1790. 
1791. 
1792. 
1793. 
1794. 
1795./** ************************************************************************
1796.*  Simple class that holds item's ID and a hashmap of parameters.          *
1797.***************************************************************************/
1798.abstract class DwFwIdAndProperties {
1799. 
1800.        function DwFwIdAndProperties($iId=null){
1801.                $this->SetId($iId);
1802.        }
1803. 
1804.        var $sId;
1805.        function GetId()    { return $this->sId; }
1806.        function SetId($sId){ $this->sId = $sId; }
1807. 
1808.        var $asParams = Array();
1809.        function SetProperty($sName, $sValue){ $this->asParams[(string)$sName] = $sValue; }
1810.        function GetProperty($sName){ return isset($this->asParams[(string)$sName]) ? $this->asParams[(string)$sName] : null; }
1811.        //function HasProperty($sName){ return isset($this->asParams[(string)$sName]); }
1812.        function HasProperty($sName){ return array_key_exists((string)$sName, $this->asParams); }
1813.        function IsPropertySet($sName){ return $this->HasProperty($sName); }
1814. 
1815.        function GetProperties(){ return $this->asParams; }
1816.        function SetProperties($asParams, $bOverwrite=true){
1817.                foreach( $asParams as $sName => $sValue ){
1818.                        if($bOverwrite || !isset($this->asParams[(string)$sName]))
1819.                                $this->asParams[(string)$sName] = $sValue;
1820.                }
1821.        }
1822. 
1823.        function DwFwIdAndParams($sId=null){ $this->sId = $sId; }
1824. 
1825.}// class DwFwIdAndParams

Usage – model

01./** ************************************************************************
02.*  Simple class that holds item's ID and a hashmap of parameters.          *
03.***************************************************************************/
04.abstract class DwFwIdAndProperties {
05. 
06.        function DwFwIdAndProperties($iId=null){
07.                $this->SetId($iId);
08.        }
09. 
10.        var $sId;
11.        function GetId()    { return $this->sId; }
12.        function SetId($sId){ $this->sId = $sId; }
13. 
14.        var $asParams = Array();
15.        function SetProperty($sName, $sValue){ $this->asParams[(string)$sName] = $sValue; }
16.        function GetProperty($sName){ return isset($this->asParams[(string)$sName]) ? $this->asParams[(string)$sName] : null; }
17.        //function HasProperty($sName){ return isset($this->asParams[(string)$sName]); }
18.        function HasProperty($sName){ return array_key_exists((string)$sName, $this->asParams); }
19.        function IsPropertySet($sName){ return $this->HasProperty($sName); }
20. 
21.        function GetProperties(){ return $this->asParams; }
22.        function SetProperties($asParams, $bOverwrite=true){
23.                foreach( $asParams as $sName => $sValue ){
24.                        if($bOverwrite || !isset($this->asParams[(string)$sName]))
25.                                $this->asParams[(string)$sName] = $sValue;
26.                }
27.        }
28. 
29.        function DwFwIdAndParams($sId=null){ $this->sId = $sId; }
30. 
31.}// class DwFwIdAndParams
32. 
33. 
34. 
35. 
36. 
37./***************************************************************************
38.*  User                                                                    *
39.***************************************************************************/
40.class cObjectPersistenceTestClass_User extends DwFwIdAndProperties {
41.        function GetProperty($sName) {
42.                if( 'id' == $sName ) return $this->GetId();
43.                else                 return parent::GetProperty($sName);
44.        }
45.        function SetProperty($sName, $sValue){
46.                if( 'id' == $sName ) return $this->SetId($sValue);
47.                else                 return parent::SetProperty($sName, $sValue);
48.        }
49.}// class cObjectPersistenceTestClass_User
50. 
51./***************************************************************************
52.*  Door                                                                    *
53.***************************************************************************/
54.class cObjectPersistence_TestClass_Door extends DwFwIdAndProperties {
55.        function GetProperty($sName) {
56.                if( 'id' == $sName ) return $this->GetId();
57.                else                 return parent::GetProperty($sName);
58.        }
59.        function SetProperty($sName, $sValue){
60.                if( 'id' == $sName ) return $this->SetId($sValue);
61.                else                 return parent::SetProperty($sName, $sValue);
62.        }
63.}// class cObjectPersistenceTestClass_User
64. 
65./***************************************************************************
66.*  Key                                                                     *
67.***************************************************************************/
68.class cObjectPersistence_TestClass_Key extends DwFwIdAndProperties {
69.        function GetProperty($sName) {
70.                if( 'id' == $sName ) return $this->GetId();
71.                else                 return parent::GetProperty($sName);
72.        }
73.        function SetProperty($sName, $sValue){
74.                if( 'id' == $sName ) return $this->SetId($sValue);
75.                else                 return parent::SetProperty($sName, $sValue);
76.        }
77.}// class cObjectPersistenceTestClass_User

Usage – data manipulation

01.// Create DB object
02.$oDB = new cDBAccess_MySQL('localhost:3350', 'test', 'test', 'test', 'cp1250');
03.$oDB->SetSelectMode(CDBA_SELECT_RETURNS_CRESULT_ON_ERROR);
04.echo "Connect: ".///
05.        $oDB->Connect();
06.echo "<br/>\n";///
07. 
08.//$xVal = $oDB->SelectCell("SELECT NULL"); // Test what NULL looks like in PHP
09.//echo "\n".gettype($xVal)." ".ord($xVal); die();
10. 
11.// Create Object Persistence object
12.$oOP = new cDwObjectPersistence_DB($oDB);
13.//$oColTypes =& new cDwOpColumnTypesRepository();

Example SQL schema & data

01.CREATE TABLE `ap_users` (
02.  `id` int(10) unsigned NOT NULL auto_increment,
03.  `user` varchar(255) NOT NULL,
04.  `pass` varchar(40) default NULL,
05.  `fname` varchar(255) NOT NULL,
06.  `lname` varchar(255) NOT NULL,
07.  `addr` varchar(255) NOT NULL,
08.  `city` varchar(255) NOT NULL,
09.  `state` enum('good', 'bad') NOT NULL DEFAULT 'good',
10.  `psc` varchar(255) NOT NULL,
11.  `ctry` tinyint(3) unsigned NOT NULL,
12.  `ppal` varchar(255) default NULL
13.);
14.INSERT INTO ap_users (id, user) VALUES
15.  (10001, 'Ondra')
16., (10002, 'Zdenek')
17., (10003, 'Martin')
18., (10004, 'Zuzka')
19., (10005, 'David')
20., (10006, 'Satan')
21.;
22. 
23.CREATE TABLE ap_doors (
24.        id INT UNSIGNED NOT NULL auto_increment PRIMARY KEY,
25.        txt VARCHAR(15)
26.);
27.INSERT INTO ap_doors VALUES (330, 'Nebe'), (118, 'Bazina'), (116, 'Peklo'), (110, 'Curaprox'), (222, 'Neznamo');
28. 
29.CREATE TABLE ap_keys (
30.        id_user INT UNSIGNED NOT NULL,
31.        id_door INT UNSIGNED NOT NULL,
32.        PRIMARY KEY pk( id_user, id_door )
33.);
34.INSERT INTO ap_keys VALUES
35.(10001, 330), (10001, 118), (10001, 110),    -- Ondra
36.(10002, 118), (10002, 116),                  -- Zdenek
37.(10003, 118), (10003, 330), (10003, 116),    -- Martin
38.(10004, 110), (10004, 330),                  -- Zuzka
39.(10006, 116), (10006, 118);                  -- Satan
01.$oClass = new cDwOpClass('cObjectPersistenceTestClass_User', 'ap_users');
02.//$oProp = $oClass->AddProperty('id', $oColTypes->GetColumnType('INT UNSIGNED'));
03.        $oProp = $oClass->AddProperty('id', new cDwOpColumnType_INT_UNSIGNED() );
04.        $oClass->SetIdProperty($oProp);
05.        $oProp = $oClass->AddProperty('user'new cDwOpColumnType_VARCHAR(255, DWOP_NOT_NULL) );
06.        $oProp = $oClass->AddProperty('pass'new cDwOpColumnType_VARCHAR(40,  DWOP_NOT_NULL) );
07.        $oProp = $oClass->AddProperty('fname', new cDwOpColumnType_VARCHAR(255, DWOP_NOT_NULL) );
08.        $oProp = $oClass->AddProperty('lname', new cDwOpColumnType_VARCHAR(255, DWOP_NOT_NULL) );
09.        $oProp = $oClass->AddProperty('addr'new cDwOpColumnType_VARCHAR(255, DWOP_NOT_NULL) );
10.        $oProp = $oClass->AddProperty('city'new cDwOpColumnType_VARCHAR(255, DWOP_NOT_NULL) );
11.        $oProp = $oClass->AddProperty('state', new cDwOpColumnType_VARCHAR(255, DWOP_NOT_NULL) );
12.        $oProp = $oClass->AddProperty('psc',   new cDwOpColumnType_VARCHAR(255, DWOP_NOT_NULL) );
13.        $oProp = $oClass->AddProperty('ctry'new cDwOpColumnType_TINYINT_UNSIGNED() );
14.        $oProp = $oClass->AddProperty('ppal'new cDwOpColumnType_VARCHAR(255) );
15.        $oClass->SetOrderProperties('lname, +fname, -id');
16.$oOP->AddClassIntoDictionary($oClass);
17./*/
18.$oClass = $oOP->CreateClassByTable('cObjectPersistenceTestClass_User', 'ap_users');
19.$oClass->SetOrderProperties('lname, +fname, -id');
20.//file_put_contents('x1.txt', AdjustedPrintR($oClass));///
21.//echo "<pre>\$oClass: ".AdjustedPrintR($oClass)."</pre>";///

Usage – data manipulation

001.srand(time());
002. 
003.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
004. 
005.// Load by ID
006.echo "<h3>Load by ID</h3>";
007.$iID = 1;
008.echo "<pre>\$oObject = \$oOP->LoadObjectById(".$oClass->GetName().", $iID);</pre>";
009.$oUser = $oOP->LoadObjectById($oClass, $iID);
010.echo "<pre>oUser: [".gettype($oUser)."]".AdjustedPrintR($oUser)."</pre>";
011.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
012. 
013. 
014.// Load by ID, using class name string
015.echo "<h3>Load by ID, using class name string</h3>";
016.$iID = 1;
017.echo "<pre>\$oObject =& \$oOP->LoadObjectById('".$oClass->GetName()."', $iID);</pre>";
018.$oUser =& $oOP->LoadObjectById($oClass->GetName(), $iID);
019.echo "<pre>oUser: [".gettype($oUser)."]".AdjustedPrintR($oUser)."</pre>";
020.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
021. 
022. 
023. 
024.// Load by value
025.echo "<h3>Load by value</h3>";
026.$xVal = 'as';
027.echo "<pre>\$aoUsers = \$oOP->LoadObjectsByValue(".$oClass->GetName().", 'user', $xVal);</pre>";
028.$aoUsers = $oOP->LoadObjectsByValue($oClass, 'user', $xVal);
029.echo "<pre>\$aoUsers: [".gettype($aoUsers)."]".AdjustedPrintR($aoUsers)."</pre>";
030.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
031. 
032. 
033. 
034.// Save
035.echo "<h3>Save</h3>";
036.$oUser = new cObjectPersistenceTestClass_User();
037.$oUser->SetId(1);
038.$oUser->SetProperty('user', 'as'.rand());
039.$oUser->SetProperty('pass', 'as');
040.$oUser->SetProperty('fname', 'Astar');
041.$oUser->SetProperty('lname', 'Seran');
042.echo "<pre>oUser: [".gettype($oUser)."]".AdjustedPrintR($oUser)."</pre>";
043.echo "<pre>\$oOP->SaveObject(\$oUser);</pre>";
044.$bSucc = $oOP->SaveObject($oUser);
045.echo "<div>".($bSucc ? 'saved' : 'error')."</div>";
046.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
047. 
048. 
049.// Create
050.echo "<h3>Create</h3>";
051.$oUser = new cObjectPersistenceTestClass_User();
052.$oUser->SetId(null);
053.$oUser->SetProperty('user', 'as'.rand());
054.$oUser->SetProperty('pass', 'as');
055.$oUser->SetProperty('fname', 'Astar');
056.$oUser->SetProperty('lname', 'Seran');
057.echo "<pre>oUser: [".gettype($oUser)."]".AdjustedPrintR($oUser)."</pre>";
058.echo "<pre>\$oOP->SaveObject(\$oUser);</pre>";
059.$bSucc = $oOP->SaveObject($oUser);
060.echo "<div>".($bSucc ? 'saved' : 'error')."</div>";
061.if(!$bSucc) echo "<pre>".$oOP->GetDB()->GetLastErrorString()."</pre>";
062.echo "<pre>\$oUser: [".gettype($oUser)."]".AdjustedPrintR($oUser)."</pre>";
063.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
064. 
065. 
066.// Load by value - load object created above -> Object Pool hit
067.echo "<h3>Load by value 2</h3>";
068. 
069.$xVal = $oUser->GetProperty('user');
070.echo "<pre>\$aoUsers = \$oOP->LoadObjectsByValue(".$oClass->GetName().", 'user', $xVal);</pre>";
071.$aoUsers = $oOP->LoadObjectsByValue($oClass, 'user', $xVal);
072.echo "<pre>\$aoUsers: [".gettype($aoUsers)."]".AdjustedPrintR($aoUsers)."</pre>";
073.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
074. 
075. 
076. 
077.// Remove from pool - still that one object
078.echo "<h3>Remove from pool</h3>";
079. 
080.echo "<pre>\$bSucc = \$oOP->RemObjectFromPool(\$oUser);</pre>";
081.$bSucc = $oOP->RemObjectFromPool($oUser);
082.echo "<pre>\$oUser: [".gettype($oUser)."]".AdjustedPrintR($oUser)."</pre>";
083.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
084. 
085. 
086.// Load by value - that object again
087.echo "<h3>Load by value 3</h3>";
088. 
089.$xVal = $oUser->GetProperty('user');
090.echo "<pre>\$aoUsers = \$oOP->LoadObjectsByValue(".$oClass->GetName().", 'user', $xVal);</pre>";
091.$aoUsers = $oOP->LoadObjectsByValue($oClass, 'user', $xVal);
092.echo "<pre>\$aoUsers: [".gettype($aoUsers)."]".AdjustedPrintR($aoUsers)."</pre>";
093.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
094. 
095.// Delete that object from DB
096.echo "<h3>Delete object by ID</h3>";
097. 
098.echo "<pre>\$oOP->DeleteObject(".$oClass->GetName().", ".$oUser->GetId().");</pre>";
099.$bSucc = $oOP->DeleteObject($oClass, $oUser->GetId());
100.if(!$bSucc) echo '<div style="font-color: red;">Delete failed. Error: '.$oOP->GetError()."</pre>";
101.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
102. 
103. 
104. 
105.// Load all objects
106.echo "<h3>Load all objects</h3>";
107. 
108.echo "<pre>\$aoUsers = \$oOP->LoadObjects(".$oClass->GetName().");</pre>";
109.$aoUsers = $oOP->LoadObjects($oClass);
110.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
111.//echo "<pre>\$aoUsers: [".gettype($aoUsers)."]".AdjustedPrintR($aoUsers)."</pre>";
112. 
113. 
114. 
115. 
116.// M : N relation
117.$oClassUser = $oClass;
118.echo "<h2>M : N Relation</h2>";
119. 
120.echo "<div>\$oClassDoor = \$oOP->CreateClassByTable('cObjectPersistence_TestClass_Door', 'ap_doors');</div>";
121.$oClassDoor = $oOP->CreateClassByTable('cObjectPersistence_TestClass_Door', 'ap_doors');
122.file_put_contents('oClassDoor.txt', AdjustedPrintR($oClassDoor));///
123.echo "<div>\$oClassDoor->IsSuitableForMnRelationSide(): ".(int)$oClassDoor->IsSuitableForMnRelationSide()."</div>";
124.echo "<div>\$oClassUser->IsSuitableForMnRelationSide(): ".(int)$oClassUser->IsSuitableForMnRelationSide()."</div>";
125. 
126.echo "<div>\$oClassKey  = \$oOP->CreateClassByTable('cObjectPersistence_TestClass_Key', 'ap_keys');</div>";
127.$oClassKey  = $oOP->CreateClassByTable('cObjectPersistence_TestClass_Key', 'ap_keys');
128.file_put_contents('oClassKey.txt', AdjustedPrintR($oClassKey));///
129.echo "\$oClassKey->IsSuitableForMnRelationGlue(): ".(int)$oClassKey->IsSuitableForMnRelationGlue();
130. 
131.// Settin'up
132.echo '<pre>$oOP->SetMnRelation($oClassUser, $oClassKey, $oClassDoor);</pre>';
133.$bSucc = $oOP->SetMnRelation($oClassUser, $oClassKey, $oClassDoor);
134.file_put_contents('oClassKey2.txt', AdjustedPrintR($oClassKey));///
135.if(!$bSucc) echo "<div>Error: ".$oOP->GetError()."</div>";
136. 
137. 
138. 
139.// Load objects by M : N relation
140. 
141./*/ Determines the Class and the ID from the $oUser object.
142.echo "<pre>\$aoDoors = \$oOP->LoadObjectsByMnRelation(\$oUser, \$oClassKey, \$oClassDoor)</pre>";
143.$aoDoors = $oOP->LoadObjectsByMnRelation($oUser, $oClassKey, $oClassDoor);
144.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
145.echo "<pre>\$aoDoors: (".gettype($aoDoors).") ".AdjustedPrintR($aoDoors)."</pre>";
146./*/ // The same with ID explicitly set to 10001
147.echo "<pre>\$aoDoors = \$oOP->LoadObjectsByMnRelation('cObjectPersistenceTestClass_User', \$oClassKey, \$oClassDoor, 10001)</pre>";
148.$aoDoors = $oOP->LoadObjectsByMnRelation('cObjectPersistenceTestClass_User', $oClassKey, $oClassDoor, 10001);
149.echo "<div>GetPoolCount(): ".$oOP->GetPoolCount()."</div>";
150.echo "<pre>\$aoDoors: (".gettype($aoDoors).") ".AdjustedPrintR($aoDoors)."</pre>";
151. 
152.// Multiple ID properties SQL for an object:
153.echo "<div>\$oClassDoor->GetIdPropertiesSql(\$aoDoors[0]): ".$oClassDoor->GetIdPropertiesSql($aoDoors[0])."</pre>";
154. 
155.$aoKeys = $oOP->LoadObjectsByValue('cObjectPersistence_TestClass_Key', 'id_user', 10001);
156.echo "<div>\$oClassKey->GetIdPropertiesSql(\$aoDoors[0]): ".$oClassKey->GetIdPropertiesSql($aoKeys[0])."</pre>";
157. 
158. 
159. 
160.if( $oOP->GetError() )
161.echo '<div style="color: red">Error from the past: '.$oOP->GetError()."</div>";

0