diff --git a/README.textile b/README.textile index 843025d..d8136f3 100644 --- a/README.textile +++ b/README.textile @@ -3,6 +3,8 @@ h1. MongoRecord MongoRecord is a PHP Mongo ORM layer built on top of the PHP "Mongo PECL extension":http://pecl.php.net/package/mongo MongoRecord is an extraction from online classifieds site "Oodle":http://www.oodle.com. Oodle's requirements for a manageable, easy to understand interface for dealing with the super-scalable Mongo datastore was the primary reason for MongoRecord. It was developed to use with PHP applications looking to add Mongo's scaling capabilities while dealing with a nice abstraction layer. +*This branch is folked from lunaru/MongoRecord(https://github.com/lunaru/MongoRecord), and add relation features(has_many,has_one,belong_to)* + h2. Features @@ -11,6 +13,7 @@ h2. Features * Validations * Callbacks * Sorting, offsets, limits +* Relations(has_many,has_one,belong_to) h2. Requirements @@ -104,6 +107,64 @@ $person = new Person(); $person->setName("Bob"); $person->save(); // fails! +h3. Relations + +Relations can be set to support belong_to,has_many,has_one, see below sample: + +pre.. +class Student extends BaseMongoRecord +{ + protected $belong_to=array('School'); + /** + can add more targets with-> $belong_to=array('ClsA','ClsB',...) + below methods are genereated automaticlly: + $this->getSchool();//return school + $this->setSchool($school);//return $this + */ +} + +class School extends BaseMongoRecord +{ + protected $has_many=array('Student'); + /** + Can add more targets with-> $has_many=array('ClsA','ClsB',...) + below methods are genereated automaticlly: + $this->getStudents();//return MongoRecordIterator + $this->setStudents($stuarray);//parm is array of students,return $this + $this->createStudent();//new student and return it,return student + $this->addStudent($stu);//parm is student instante,return $this + $this->removeStudent($stu);//parm is student instant,return $this + */ +} + +$school=new School(); +$school->setName('fooschool'); +$school->createStudent()->setName("foostu1")->save(); +$school->createStudent()->setName("foostu2")->save(); +$school->save(); + +$students=$school->getStudents();//=>MongoRecordIterator +var_dump($students->count());//=>2 +foreach($students as $student) + { + var_dump($student);//=>foostu1,foostu2 + } + +$student=new Student(); +$student->setName('foostu3')->save(); +$school->addStudent($student); +$students=$school->getStudents();//=>MongoRecordIterator + +var_dump($students->count());//=>3 +foreach($students as $stu) + { + var_dump($stu);//=>foostu1,foostu2,foostu3 + } + +$student->setSchool($school)->save(); +var_dump($student->getSchool()->getName());//=>'fooschool' + + h3. Callbacks Callbacks can be added for the following events: diff --git a/lib/#Inflector.php# b/lib/#Inflector.php# new file mode 100644 index 0000000..d7472cc --- /dev/null +++ b/lib/#Inflector.php# @@ -0,0 +1,543 @@ +test(); + * * + * * @package cake + * * @subpackage cake.cake.libs + * * @link http://book.cakephp.org/view/491/Inflector + * */ +class Inflector { + /** + * * Pluralized words. + * * + * * @var array + * * @access private + * **/ + var $pluralized = array(); + /** + * * List of pluralization rules in the form of pattern => replacement. + * * + * * @var array + * * @access public + * * @link http://book.cakephp.org/view/47/Custom-Inflections + * **/ + var $pluralRules = array(); + /** + * * Singularized words. + * * + * * @var array + * * @access private + * **/ + var $singularized = array(); + /** + * * List of singularization rules in the form of pattern => replacement. + * * + * * @var array + * * @access public + * * @link http://book.cakephp.org/view/47/Custom-Inflections + * **/ + var $singularRules = array(); + /** + * * Plural rules from inflections.php + * * + * * @var array + * * @access private + * **/ + var $__pluralRules = array(); + /** + * * Un-inflected plural rules from inflections.php + * * + * * @var array + * * @access private + * **/ + var $__uninflectedPlural = array(); + /** + * * Irregular plural rules from inflections.php + * * + * * @var array + * * @access private + * **/ + var $__irregularPlural = array(); + /** + * * Singular rules from inflections.php + * * + * * @var array + * * @access private + * **/ + var $__singularRules = array(); + /** + * * Un-inflectd singular rules from inflections.php + * * + * * @var array + * * @access private + * **/ + var $__uninflectedSingular = array(); + /** + * * Irregular singular rules from inflections.php + * * + * * @var array + * * @access private + * **/ + var $__irregularSingular = array(); + /** + * * Gets a reference to the Inflector object instance + * * + * * @return object + * * @access public + * */ + static function getInstance() { + static $instance = array(); + + if (!$instance) { + $instance[0] = new Inflector(); + /*if (file_exists(CONFIGS.'inflections.php')) { + include(CONFIGS.'inflections.php'); + $instance[0]->__pluralRules = $pluralRules; + $instance[0]->__uninflectedPlural = $uninflectedPlural; + $instance[0]->__irregularPlural = $irregularPlural; + $instance[0]->__singularRules = $singularRules; + $instance[0]->__uninflectedSingular = $uninflectedPlural; + $instance[0]->__irregularSingular = array_flip($irregularPlural); + }*/ + } + return $instance[0]; + } + /** + * * Initializes plural inflection rules. + * * + * * @return void + * * @access private + * */ + function __initPluralRules() { + $corePluralRules = array( + '/(s)tatus$/i' => '\1\2tatuses', + '/(quiz)$/i' => '\1zes', + '/^(ox)$/i' => '\1\2en', + '/([m|l])ouse$/i' => '\1ice', + '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', + '/(x|ch|ss|sh)$/i' => '\1es', + '/([^aeiouy]|qu)y$/i' => '\1ies', + '/(hive)$/i' => '\1s', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/sis$/i' => 'ses', + '/([ti])um$/i' => '\1a', + '/(p)erson$/i' => '\1eople', + '/(m)an$/i' => '\1en', + '/(c)hild$/i' => '\1hildren', + '/(buffal|tomat)o$/i' => '\1\2oes', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/us$/' => 'uses', + '/(alias)$/i' => '\1es', + '/(ax|cris|test)is$/i' => '\1es', + '/s$/' => 's', + '/^$/' => '', + '/$/' => 's'); + + $coreUninflectedPlural = array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', 'Amoyese', + 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', + 'cod', 'coitus', 'Congoese', 'contretemps', 'corps', 'debris', 'diabetes', 'djinn', 'eland', 'elk', + 'equipment', 'Faroese', 'flounder', 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti', + 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'Kiplingese', + 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media', 'mews', 'moose', 'mumps', 'Nankingese', 'news', + 'nexus', 'Niasese', 'Pekingese', 'People', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', 'proceedings', + 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass', 'series', 'Shavese', 'shears', + 'siemens', 'species', 'swine', 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', + 'whiting', 'wildebeest', 'Yengeese'); + + $coreIrregularPlural = array( + 'atlas' => 'atlases', + 'beef' => 'beefs', + 'brother' => 'brothers', + 'child' => 'children', + 'corpus' => 'corpuses', + 'cow' => 'cows', + 'ganglion' => 'ganglions', + 'genie' => 'genies', + 'genus' => 'genera', + 'graffito' => 'graffiti', + 'hoof' => 'hoofs', + 'loaf' => 'loaves', + 'man' => 'men', + 'money' => 'monies', + 'mongoose' => 'mongooses', + 'move' => 'moves', + 'mythos' => 'mythoi', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'penis' => 'penises', + 'person' => 'people', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'testis' => 'testes', + 'trilby' => 'trilbys', + 'turf' => 'turfs'); + + /*$pluralRules = Set::pushDiff($this->__pluralRules, $corePluralRules); + $uninflected = Set::pushDiff($this->__uninflectedPlural, $coreUninflectedPlural); + $irregular = Set::pushDiff($this->__irregularPlural, $coreIrregularPlural); + */ + + $pluralRules = $corePluralRules; + $uninflected = $coreUninflectedPlural; + $irregular = $coreIrregularPlural; + + $this->pluralRules = array('pluralRules' => $pluralRules, 'uninflected' => $uninflected, 'irregular' => $irregular); + $this->pluralized = array(); + } + /** + * * Return $word in plural form. + * * + * * @param string $word Word in singular + * * @return string Word in plural + * * @access public + * * @static + * * @link http://book.cakephp.org/view/572/Class-methods + * */ + function pluralize($word) { + $_this = self::getInstance(); + if (!isset($_this->pluralRules) || empty($_this->pluralRules)) { + $_this->__initPluralRules(); + } + + if (isset($_this->pluralized[$word])) { + return $_this->pluralized[$word]; + } + extract($_this->pluralRules); + + if (!isset($regexUninflected) || !isset($regexIrregular)) { + $regexUninflected = __enclose(implode( '|', $uninflected)); + $regexIrregular = __enclose(implode( '|', array_keys($irregular))); + $_this->pluralRules['regexUninflected'] = $regexUninflected; + $_this->pluralRules['regexIrregular'] = $regexIrregular; + } + + if (preg_match('/^(' . $regexUninflected . ')$/i', $word, $regs)) { + $_this->pluralized[$word] = $word; + return $word; + } + + if (preg_match('/(.*)\\b(' . $regexIrregular . ')$/i', $word, $regs)) { + $_this->pluralized[$word] = $regs[1] . substr($word, 0, 1) . substr($irregular[strtolower($regs[2])], 1); + return $_this->pluralized[$word]; + } + + foreach ($pluralRules as $rule => $replacement) { + if (preg_match($rule, $word)) { + $_this->pluralized[$word] = preg_replace($rule, $replacement, $word); + return $_this->pluralized[$word]; + } + } + } + /** + * * Initializes singular inflection rules. + * * + * * @return void + * * @access protected + * */ + function __initSingularRules() { + $coreSingularRules = array( + '/(s)tatuses$/i' => '\1\2tatus', + '/^(.*)(menu)s$/i' => '\1\2', + '/(quiz)zes$/i' => '\\1', + '/(matr)ices$/i' => '\1ix', + '/(vert|ind)ices$/i' => '\1ex', + '/^(ox)en/i' => '\1', + '/(alias)(es)*$/i' => '\1', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', + '/([ftw]ax)es/' => '\1', + '/(cris|ax|test)es$/i' => '\1is', + '/(shoe)s$/i' => '\1', + '/(o)es$/i' => '\1', + '/ouses$/' => 'ouse', + '/uses$/' => 'us', + '/([m|l])ice$/i' => '\1ouse', + '/(x|ch|ss|sh)es$/i' => '\1', + '/(m)ovies$/i' => '\1\2ovie', + '/(s)eries$/i' => '\1\2eries', + '/([^aeiouy]|qu)ies$/i' => '\1y', + '/([lr])ves$/i' => '\1f', + '/(tive)s$/i' => '\1', + '/(hive)s$/i' => '\1', + '/(drive)s$/i' => '\1', + '/([^fo])ves$/i' => '\1fe', + '/(^analy)ses$/i' => '\1sis', + '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', + '/([ti])a$/i' => '\1um', + '/(p)eople$/i' => '\1\2erson', + '/(m)en$/i' => '\1an', + '/(c)hildren$/i' => '\1\2hild', + '/(n)ews$/i' => '\1\2ews', + '/eaus$/' => 'eau', + '/^(.*us)$/' => '\\1', + '/s$/i' => ''); + + $coreUninflectedSingular = array( + '.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox', '.*sheep', '.*ss', 'Amoyese', + 'bison', 'Borghese', 'bream', 'breeches', 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', + 'cod', 'coitus', 'Congoese', 'contretemps', 'corps', 'debris', 'diabetes', 'djinn', 'eland', 'elk', + 'equipment', 'Faroese', 'flounder', 'Foochowese', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'graffiti', + 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'Kiplingese', + 'Kongoese', 'Lucchese', 'mackerel', 'Maltese', 'media', 'mews', 'moose', 'mumps', 'Nankingese', 'news', + 'nexus', 'Niasese', 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'pliers', 'Portuguese', 'proceedings', + 'rabies', 'rice', 'rhinoceros', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass', 'series', 'Shavese', 'shears', + 'siemens', 'species', 'swine', 'testes', 'trousers', 'trout', 'tuna', 'Vermontese', 'Wenchowese', + 'whiting', 'wildebeest', 'Yengeese' + ); + + $coreIrregularSingular = array( + 'atlases' => 'atlas', + 'beefs' => 'beef', + 'brothers' => 'brother', + 'children' => 'child', + 'corpuses' => 'corpus', + 'cows' => 'cow', + 'ganglions' => 'ganglion', + 'genies' => 'genie', + 'genera' => 'genus', + 'graffiti' => 'graffito', + 'hoofs' => 'hoof', + 'loaves' => 'loaf', + 'men' => 'man', + 'monies' => 'money', + 'mongooses' => 'mongoose', + 'moves' => 'move', + 'mythoi' => 'mythos', + 'numina' => 'numen', + 'occiputs' => 'occiput', + 'octopuses' => 'octopus', + 'opuses' => 'opus', + 'oxen' => 'ox', + 'penises' => 'penis', + 'people' => 'person', + 'sexes' => 'sex', + 'soliloquies' => 'soliloquy', + 'testes' => 'testis', + 'trilbys' => 'trilby', + 'turfs' => 'turf', + 'waves' => 'wave' + ); + + /*$singularRules = Set::pushDiff($this->__singularRules, $coreSingularRules); + $uninflected = Set::pushDiff($this->__uninflectedSingular, $coreUninflectedSingular); + $irregular = Set::pushDiff($this->__irregularSingular, $coreIrregularSingular); + */ + + $singularRules = $coreSingularRules; + $uninflected = $coreUninflectedSingular; + $irregular = $coreIrregularSingular; + + $this->singularRules = array('singularRules' => $singularRules, 'uninflected' => $uninflected, 'irregular' => $irregular); + $this->singularized = array(); + } + /** + * * Return $word in singular form. + * * + * * @param string $word Word in plural + * * @return string Word in singular + * * @access public + * * @static + * * @link http://book.cakephp.org/view/572/Class-methods + * */ + function singularize($word) { + $_this = self::getInstance(); + if (!isset($_this->singularRules) || empty($_this->singularRules)) { + $_this->__initSingularRules(); + } + + if (isset($_this->singularized[$word])) { + return $_this->singularized[$word]; + } + extract($_this->singularRules); + + if (!isset($regexUninflected) || !isset($regexIrregular)) { + $regexUninflected = __enclose(implode( '|', $uninflected)); + $regexIrregular = __enclose(implode( '|', array_keys($irregular))); + $_this->singularRules['regexUninflected'] = $regexUninflected; + $_this->singularRules['regexIrregular'] = $regexIrregular; + } + + if (preg_match('/^(' . $regexUninflected . ')$/i', $word, $regs)) { + $_this->singularized[$word] = $word; + return $word; + } + + if (preg_match('/(.*)\\b(' . $regexIrregular . ')$/i', $word, $regs)) { + $_this->singularized[$word] = $regs[1] . substr($word, 0, 1) . substr($irregular[strtolower($regs[2])], 1); + return $_this->singularized[$word]; + } + + foreach ($singularRules as $rule => $replacement) { + if (preg_match($rule, $word)) { + $_this->singularized[$word] = preg_replace($rule, $replacement, $word); + return $_this->singularized[$word]; + } + } + $_this->singularized[$word] = $word; + return $word; + } + /** + * * Returns the given lower_case_and_underscored_word as a CamelCased word. + * * + * * @param string $lower_case_and_underscored_word Word to camelize + * * @return string Camelized word. LikeThis. + * * @access public + * * @static + * * @link http://book.cakephp.org/view/572/Class-methods + * */ + function camelize($lowerCaseAndUnderscoredWord) { + return str_replace(" ", "", ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord))); + } + /** + * * Returns the given camelCasedWord as an underscored_word. + * * + * * @param string $camelCasedWord Camel-cased word to be "underscorized" + * * @return string Underscore-syntaxed version of the $camelCasedWord + * * @access public + * * @static + * * @link http://book.cakephp.org/view/572/Class-methods + * */ + function underscore($camelCasedWord) { + return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord)); + } + /** + * * Returns the given underscored_word_group as a Human Readable Word Group. + * * (Underscores are replaced by spaces and capitalized following words.) + * * + * * @param string $lower_case_and_underscored_word String to be made more readable + * * @return string Human-readable string + * * @access public + * * @static + * * @link http://book.cakephp.org/view/572/Class-methods + * */ + function humanize($lowerCaseAndUnderscoredWord) { + return ucwords(str_replace("_", " ", $lowerCaseAndUnderscoredWord)); + } + /** + * * Returns corresponding table name for given model $className. ("people" for the model class "Person"). + * * + * * @param string $className Name of class to get database table name for + * * @return string Name of the database table for given class + * * @access public + * * @static + * * @link http://book.cakephp.org/view/572/Class-methods + * */ + function tableize($className) { + return Inflector::pluralize(Inflector::underscore($className)); + } + /** + * * Returns Cake model class name ("Person" for the database table "people".) for given database table. + * * + * * @param string $tableName Name of database table to get class name for + * * @return string Class name + * * @access public + * * @static + * * @link http://book.cakephp.org/view/572/Class-methods + * */ + function classify($tableName) { + return Inflector::camelize(Inflector::singularize($tableName)); + } + /** + * * Returns camelBacked version of an underscored string. + * * + * * @param string $string + * * @return string in variable form + * * @access public + * * @static + * * @link http://book.cakephp.org/view/572/Class-methods + * */ + function variable($string) { + $string = Inflector::camelize(Inflector::underscore($string)); + $replace = strtolower(substr($string, 0, 1)); + return $replace . substr($string, 1); + } + + + + + /** + * * Returns a string with all spaces converted to underscores (by default), accented + * * characters converted to non-accented characters, and non word characters removed. + * * + * * @param string $string + * * @param string $replacement + * * @return string + * * @access public + * * @static + * * @link http://book.cakephp.org/view/572/Class-methods + * */ + function slug($string, $replacement = '_') { + if (!class_exists('String')) { + require LIBS . 'string.php'; + } + $map = array( + '/à|á|å|â/' => 'a', + '/è|é|ê|ẽ|ë/' => 'e', + '/ì|í|î/' => 'i', + '/ò|ó|ô|ø/' => 'o', + '/ù|ú|ů|û/' => 'u', + '/ç/' => 'c', + '/ñ/' => 'n', + '/ä|æ/' => 'ae', + '/ö/' => 'oe', + '/ü/' => 'ue', + '/Ä/' => 'Ae', + '/Ü/' => 'Ue', + '/Ö/' => 'Oe', + '/ß/' => 'ss', + '/[^\w\s]/' => ' ', + '/\\s+/' => $replacement, + String::insert('/^[:replacement]+|[:replacement]+$/', array('replacement' => preg_quote($replacement, '/'))) => '', + ); + return preg_replace(array_keys($map), array_values($map), $string); + } +} +/** + * * Enclose a string for preg matching. + * * + * * @param string $string String to enclose + * * @return string Enclosed string + * */ +function __enclose($string) { + return '(?:' . $string . ')'; +} +?> diff --git a/lib/.#Inflector.php b/lib/.#Inflector.php new file mode 120000 index 0000000..a089067 --- /dev/null +++ b/lib/.#Inflector.php @@ -0,0 +1 @@ +shaoyang@ubuntu.27158:1367142584 \ No newline at end of file diff --git a/lib/BaseMongoRecord.php b/lib/BaseMongoRecord.php index adadbb8..c73321f 100644 --- a/lib/BaseMongoRecord.php +++ b/lib/BaseMongoRecord.php @@ -5,272 +5,483 @@ require_once('Inflector.php'); abstract class BaseMongoRecord - implements MongoRecord +implements MongoRecord { - protected $attributes; - protected $errors; - private $new; - - public static $database = null; - public static $connection = null; - public static $findTimeout = 20000; - - /** - * Collection name will be generated automaticaly if setted to null. - * If overridden in child class, then new collection name uses. - * - * @var string - */ - protected static $collectionName = null; - - public function __construct($attributes = array(), $new = true) - { - $this->new = $new; - $this->attributes = $attributes; - $this->errors = array(); - - if ($new) - $this->afterNew(); - } - - public function validate() - { - $this->beforeValidation(); - $retval = $this->isValid(); - $this->afterValidation(); - return $retval; - } - - public function save(array $options = array()) - { - if (!$this->validate()) - return false; - - $this->beforeSave(); - - $collection = self::getCollection(); - $collection->save($this->attributes, $options); - - $this->new = false; - $this->afterSave(); - - return true; - } - - public function destroy() - { - $this->beforeDestroy(); - - if (!$this->new) - { - $collection = self::getCollection(); - $collection->remove(array('_id' => $this->attributes['_id'])); - } - } - private static function _find($query = array(), $options = array()){ + protected $attributes; + protected $errors; + private $new; + + public static $database = null; + public static $connection = null; + public static $findTimeout = 20000; + + /** + * Collection name will be generated automaticaly if setted to null. + * If overridden in child class, then new collection name uses. + * + * @var string + */ + protected static $collectionName = null; + protected $has_many=null; + protected $belong_to=null; + protected $has_one=null; + public function __construct($attributes = array(), $new = true) + { + $this->new = $new; + $this->attributes = $attributes; + $this->errors = array(); + + if ($new) + $this->afterNew(); + + if(isset($this->has_many)&&gettype($this->has_many)=="array") + { + call_user_func_array(array($this,"has_many"),$this->has_many); + } + if(isset($this->has_one)&&gettype($this->has_one)=="array") + { + call_user_func_array(array($this,"has_one"),$this->has_one); + } + if(isset($this->belong_to)&&gettype($this->belong_to)=="array") + { + call_user_func_array(array($this,"belong_to"),$this->belong_to); + } + + } + + public function validate() + { + $this->beforeValidation(); + $retval = $this->isValid(); + $this->afterValidation(); + return $retval; + } + + public function save(array $options = array()) + { + if (!$this->validate()) + return false; + + $this->beforeSave(); + + $collection = self::getCollection(); + $collection->save($this->attributes, $options); + + $this->new = false; + $this->afterSave(); + + return true; + } + + public function destroy() + { + $this->beforeDestroy(); + + if (!$this->new) + { + $collection = self::getCollection(); + $collection->remove(array('_id' => $this->attributes['_id'])); + } + } + private static function _find($query = array(), $options = array()){ - $collection = self::getCollection(); - if (isset($options['fields'])){ - $documents = $collection->find($query, $options['fields']); - } - else{ - $documents = $collection->find($query); - } + $collection = self::getCollection(); + if (isset($options['fields'])){ + $documents = $collection->find($query, $options['fields']); + } + else{ + $documents = $collection->find($query); + } - $className = get_called_class(); + $className = get_called_class(); - if (isset($options['sort'])) - $documents->sort($options['sort']); + if (isset($options['sort'])) + $documents->sort($options['sort']); - if (isset($options['offset'])) - $documents->skip($options['offset']); + if (isset($options['offset'])) + $documents->skip($options['offset']); - if (isset($options['limit'])) - $documents->limit($options['limit']); + if (isset($options['limit'])) + $documents->limit($options['limit']); - $documents->timeout($className::$findTimeout); - return $documents; - } - public static function findAll($query = array(), $options = array()) - { - $documents = static::_find($query, $options); - $ret = array(); - while ($documents->hasNext()) - { - $document = $documents->getNext(); - $ret[] = self::instantiate($document); - } - - return $ret; - } - - public static function find($query = array(), $options = array()) - { - $documents = static::_find($query, $options); - $className = get_called_class(); - return new MongoRecordIterator($documents, $className); - } - - public static function findOne($query = array(), $options = array()) - { - $options['limit'] = 1; - - $results = self::find($query, $options); - - if ($results) - return $results->current(); - else - return null; - } - - public static function count($query = array()) - { - $collection = self::getCollection(); - $documents = $collection->count($query); - - return $documents; - } - - private static function instantiate($document) - { - if ($document) - { - $className = get_called_class(); - return new $className($document, false); - } - else - { - return null; - } - } - - public function getID() - { - return $this->attributes['_id']; - } - - public function setID($id) - { - $this->attributes['_id'] = $id; - } - - public function __call($method, $arguments) - { - // Is this a get or a set - $prefix = strtolower(substr($method, 0, 3)); - - if ($prefix != 'get' && $prefix != 'set') - return; - - // What is the get/set class attribute - $inflector = Inflector::getInstance(); - $property = $inflector->underscore(substr($method, 3)); - - if (empty($prefix) || empty($property)) - { - // Did not match a get/set call - throw New Exception("Calling a non get/set method that does not exist: $method"); - } - - // Get - if ($prefix == "get" && array_key_exists($property, $this->attributes)) - { - return $this->attributes[$property]; - } - else if ($prefix == "get") - { - return null; - } - - // Set - if ($prefix == "set" && array_key_exists(0, $arguments)) - { - $this->attributes[$property] = $arguments[0]; - return $this; - } - else - { - throw new Exception("Calling a get/set method that does not exist: $property"); - } - } - - - // framework overrides/callbacks: - public function beforeSave() {} - public function afterSave() {} - public function beforeValidation() {} - public function afterValidation() {} - public function beforeDestroy() {} - public function afterNew() {} - - - protected function isValid() - { - $className = get_called_class(); - $methods = get_class_methods($className); - - foreach ($methods as $method) - { - if (substr($method, 0, 9) == 'validates') - { - $propertyCall = 'get' . substr($method, 9); - if (!$className::$method($this->$propertyCall())) - { - return false; - } - } - } - - return true; - } - - // core conventions - protected static function getCollection() - { - $className = get_called_class(); - - if (null !== static::$collectionName) - { - $collectionName = static::$collectionName; - } - else - { - $inflector = Inflector::getInstance(); - $collectionName = $inflector->tableize($className); - } - - if ($className::$database == null) - throw new Exception("BaseMongoRecord::database must be initialized to a proper database string"); - - if ($className::$connection == null) - throw new Exception("BaseMongoRecord::connection must be initialized to a valid Mongo object"); - - if (!($className::$connection->connected)) - $className::$connection->connect(); - - return $className::$connection->selectCollection($className::$database, $collectionName); - } - - public static function setFindTimeout($timeout) - { - $className = get_called_class(); - $className::$findTimeout = $timeout; - } - - public static function ensureIndex(array $keys, array $options = array()) - { - return self::getCollection()->ensureIndex($keys, $options); - } - - public static function deleteIndex($keys) - { - return self::getCollection()->deleteIndex($keys); - } - - public function getAttributes() - { - return $this->attributes; - } -} \ No newline at end of file + $documents->timeout($className::$findTimeout); + return $documents; + } + public static function findAll($query = array(), $options = array()) + { + $documents = static::_find($query, $options); + $ret = array(); + while ($documents->hasNext()) + { + $document = $documents->getNext(); + $ret[] = self::instantiate($document); + } + + return $ret; + } + + public static function find($query = array(), $options = array()) + { + $documents = static::_find($query, $options); + $className = get_called_class(); + return new MongoRecordIterator($documents, $className); + } + + public static function findOne($query = array(), $options = array()) + { + $options['limit'] = 1; + + $results = self::find($query, $options); + + if ($results) + return $results->current(); + else + return null; + } + + public static function count($query = array()) + { + $collection = self::getCollection(); + $documents = $collection->count($query); + + return $documents; + } + + private static function instantiate($document) + { + if ($document) + { + $className = get_called_class(); + return new $className($document, false); + } + else + { + return null; + } + } + + public function getID() + { + return $this->attributes['_id']; + } + + public function setID($id) + { + $this->attributes['_id'] = $id; + } + + public function __call($method, $arguments) + { + //if the attribute has been set as function + if(isset($this->$method)&&get_class($this->$method)=="Closure") + { + $func=$this->$method; + if(array_key_exists(0, $arguments)) + return $func($arguments[0]); + else + return $func(); + } + + // Is this a get or a set + $prefix = strtolower(substr($method, 0, 3)); + + if ($prefix != 'get' && $prefix != 'set') + return; + + // What is the get/set class attribute + $inflector = Inflector::getInstance(); + $property = $inflector->underscore(substr($method, 3)); + + if (empty($prefix) || empty($property)) + { + // Did not match a get/set call + throw New Exception("Calling a non get/set method that does not exist: $method"); + } + + // Get + if ($prefix == "get" && array_key_exists($property, $this->attributes)) + { + return $this->attributes[$property]; + } + else if ($prefix == "get") + { + return null; + } + + // Set + if ($prefix == "set" && array_key_exists(0, $arguments)) + { + $this->attributes[$property] = $arguments[0]; + return $this; + } + else + { + throw new Exception("Calling a get/set method that does not exist: $property"); + } + } + + + // framework overrides/callbacks: + + public function beforeSave() { + if(isset($this->beforeSave)&&get_class($this->beforeSave)=="Closure") + { $cb=$this->beforeSave; + $cb(); + } + if(isset($this->beforeSave_once)&&get_class($this->beforeSave_once)=="Closure") + { $cb=$this->beforeSave_once; + $cb(); + $this->beforeSave_once=null; + } + + } + + public function afterSave() { + if(isset($this->afterSave)&&get_class($this->afterSave)=="Closure") + { $cb=$this->afterSave; + $cb(); + } + if(isset($this->afterSave_once)&&get_class($this->afterSave_once)=="Closure") + { $cb=$this->afterSave_once; + $cb(); + $this->afterSave_once=null; + } + + } + public function beforeValidation() {} + public function afterValidation() {} + public function beforeDestroy() {} + public function afterNew() {} + + + protected function isValid() + { + $className = get_called_class(); + $methods = get_class_methods($className); + + foreach ($methods as $method) + { + if (substr($method, 0, 9) == 'validates') + { + $propertyCall = 'get' . substr($method, 9); + if (!$className::$method($this->$propertyCall())) + { + return false; + } + } + } + + return true; + } + + // core conventions + protected static function getCollection() + { + $className = get_called_class(); + + if (null !== static::$collectionName) + { + $collectionName = static::$collectionName; + } + else + { + $inflector = Inflector::getInstance(); + $collectionName = $inflector->tableize($className); + } + + if ($className::$database == null) + throw new Exception("BaseMongoRecord::database must be initialized to a proper database string"); + + if ($className::$connection == null) + throw new Exception("BaseMongoRecord::connection must be initialized to a valid Mongo object"); + + if (!($className::$connection->connected)) + $className::$connection->connect(); + + return $className::$connection->selectCollection($className::$database, $collectionName); + } + + public static function setFindTimeout($timeout) + { + $className = get_called_class(); + $className::$findTimeout = $timeout; + } + + public static function ensureIndex(array $keys, array $options = array()) + { + return self::getCollection()->ensureIndex($keys, $options); + } + + public static function deleteIndex($keys) + { + return self::getCollection()->deleteIndex($keys); + } + + public function getAttributes() + { + return $this->attributes; + } + /* Below part are adding the functions to support relationship,like has_many,belong_to */ + var $dyn_methods=array(); + + /** + * has_many + * @param mix + * @return void + * @comment generate getter/setter for array of target class + */ + + protected function has_many() + { + $numcls = func_num_args(); + $cls_list = func_get_args(); + $inflector = Inflector::getInstance(); + $self=$this; + foreach($cls_list as $clsname) + { + $plurclsname=$inflector->pluralize($clsname); + //add,create,remove + $funcname="add{$clsname}"; + $this->addfunc($funcname, + function($item) use($clsname,$self){ + $id=$item->getID(); + if($id==null) + { + //if id is null then recall the func after save + $item->afterSave_once=function()use ($item,$self,$clsname){ + $addx="add{$clsname}"; + $self->$addx($item); + + }; + return $self; + } + $getx="get{$clsname}ids"; + $itemids=$self->$getx(); + if($itemids==null) + $itemids=array(); + array_push($itemids,$item->getID()); + $setx="set{$clsname}ids"; + $self->$setx($itemids); + return $self; + }); + + $funcname="create{$clsname}"; + $this->addfunc($funcname, + function() use($clsname,$self){ + + $item=new $clsname(); + $addx="add{$clsname}"; + $self->$addx($item); + return $item; + }); + + $funcname="remove{$clsname}"; + $this->addfunc($funcname, + function($item) use($clsname,$self){ + $getx="get{$clsname}ids"; + $itemids=$self->$getx(); + $key=array_search($item->getID(),$itemids); + if(gettype($key)=="boolean") //no such value + return $self; + unset($itemids[$key]);//remove the id + $setx="set{$clsname}ids"; + $self->$setx($itemids); + return $self; + }); + + + $funcname="get{$plurclsname}"; + $this->addfunc($funcname, + function() use($clsname,$self){ + $getx="get{$clsname}ids"; + $itemids=$self->$getx(); + $items=$clsname::find(array('_id'=>array('$in'=>$itemids))); + return $items; //return MongoRecordIterator + }); + + $funcname="set{$plurclsname}"; + $this->addfunc($funcname, + function($items)use($clsname,$self){ + $itemids=array(); + foreach($items as $item) + { + array_push($itemids,$item->getID()); + } + if(count($itemids)==0) + return $self; + $setx="set{$clsname}ids"; + $self->$setx($itemids); + return $self; + }); + } + } + + /** + * has_one + * @param mixed + * @return void + * @comment it is same with belong_to func just with different expression + */ + + + protected function has_one() + { + $cls_list = func_get_args(); + call_user_func_array(array($this,"belong_to"),$cls_list); + } + + /** + * belong_to + * @param mixed + * @return void + * @comment generate getter/setter for the target object + */ + + protected function belong_to() + { + $numcls = func_num_args(); + $cls_list = func_get_args(); + $self=$this; + foreach($cls_list as $clsname) + { + $funcname="get{$clsname}"; + $this->addfunc($funcname, + function() use($funcname,$clsname,$self){ + $getx="{$funcname}Id"; + $result= $clsname::findOne(array('_id'=>$self->$getx())); + return $result; + }); + + $funcname="set{$clsname}"; + $this->addfunc($funcname, + function($ins) use($funcname,$self){ + $setx="{$funcname}Id"; + $self->$setx($ins->getID()); + return $self; + }); + + } + } + + /** + * addfunc + * @param String,Closure + * @return void + * @comments dynamicly add function for class + */ + + + protected function addfunc($name,$func) + { + $this->$name=$func; + // $this->dyn_methods[$name]=$func; + + } + +} + + + + diff --git a/sample/easysample.php b/sample/easysample.php new file mode 100644 index 0000000..15023ee --- /dev/null +++ b/sample/easysample.php @@ -0,0 +1,62 @@ + $belong_to=array('ClsA','ClsB',...) + below methods are genereated automaticlly: + $this->getSchool();//return school + $this->setSchool($school);//return $this + */ + + +} + +class School extends BaseMongoRecord +{ + protected $has_many=array('Student'); + /** + Can add more targets with-> $has_many=array('ClsA','ClsB',...) + below methods are genereated automaticlly: + $this->getStudents();//return MongoRecordIterator + $this->setStudents($stuarray);//parm is array of students,return $this + $this->createStudent();//new student and return it,return student + $this->addStudent($stu);//parm is student instante,return $this + $this->removeStudent($stu);//parm is student instant,return $this + */ +} +$school=new School(); +$school->setName('fooschool'); + +$school->createStudent()->setName("foostu1")->save(); +$school->createStudent()->setName("foostu2")->save(); +$school->save(); + +$students=$school->getStudents();//=>MongoRecordIterator +var_dump($students->count());//=>2 +foreach($students as $student) + { + var_dump($student); + } + +$student=new Student(); +$student->setName('foostu3')->save(); +$school->addStudent($student); +$students=$school->getStudents();//=>MongoRecordIterator +var_dump($students->count());//=>3 +foreach($students as $stu) + { + var_dump($stu); + } + +$student->setSchool($school)->save(); + +var_dump($student->getSchool()->getName());//=>'fooschool' + +?> \ No newline at end of file