diff --git a/core/DB/DB.class.php b/core/DB/DB.class.php index 9b59e88637..8847e7d6f2 100644 --- a/core/DB/DB.class.php +++ b/core/DB/DB.class.php @@ -415,14 +415,6 @@ public function setEncoding($encoding) return $this; } - public function registerUncacher(UncacherBase $uncacher) - { - $uncacher->uncache(); - if ($this->inTransaction()) { - $this->getUncacher()->merge($uncacher); - } - } - /** * @param string $savepointName * @return DB @@ -434,7 +426,7 @@ private function addSavepoint($savepointName) $this->savepointList[$savepointName] = true; return $this; - } + } /** * @param string $savepointName @@ -452,13 +444,21 @@ private function dropSavepoint($savepointName) private function checkSavepointExist($savepointName) { return isset($this->savepointList[$savepointName]); - } + } private function assertSavePointName($savepointName) { Assert::isEqual(1, preg_match('~^[A-Za-z][A-Za-z0-9]*$~iu', $savepointName)); } + public function registerUncacher(UncacherBase $uncacher) + { + $uncacher->uncache(); + if ($this->inTransaction()) { + $this->getUncacher()->merge($uncacher); + } + } + /** * @return UncachersPool */ @@ -470,8 +470,9 @@ private function getUncacher() private function triggerUncacher() { if ($this->uncacher) { - $this->uncacher->uncache(); + $uncacher = $this->uncacher; $this->uncacher = null; + $uncacher->uncache(); } } } diff --git a/core/Exceptions/DeadlockException.class.php b/core/Exceptions/DeadlockException.class.php new file mode 100644 index 0000000000..a4e8175b98 --- /dev/null +++ b/core/Exceptions/DeadlockException.class.php @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/core/OSQL/FromTable.class.php b/core/OSQL/FromTable.class.php index fe5fee2566..ade028ee22 100644 --- a/core/OSQL/FromTable.class.php +++ b/core/OSQL/FromTable.class.php @@ -15,7 +15,7 @@ * @ingroup OSQL * @ingroup Module **/ - final class FromTable implements Aliased, SQLTableName + final class FromTable implements Aliased, SQLTableName, SQLRealTableName { private $table = null; private $alias = null; @@ -85,5 +85,10 @@ public function getTable() { return $this->alias ? $this->alias : $this->table; } + + public function getRealTable() + { + return $this->table; + } } ?> \ No newline at end of file diff --git a/core/OSQL/Joiner.class.php b/core/OSQL/Joiner.class.php index 2df9c56a21..6a96001c4b 100644 --- a/core/OSQL/Joiner.class.php +++ b/core/OSQL/Joiner.class.php @@ -99,6 +99,10 @@ public function getLastTable() return null; } + public function getTables() { + return $this->from ?: array(); + } + public function toDialectString(Dialect $dialect) { $fromString = null; diff --git a/core/OSQL/SQLBaseJoin.class.php b/core/OSQL/SQLBaseJoin.class.php index 00468053b6..a2791df2ad 100644 --- a/core/OSQL/SQLBaseJoin.class.php +++ b/core/OSQL/SQLBaseJoin.class.php @@ -12,7 +12,7 @@ /** * @ingroup OSQL **/ - abstract class SQLBaseJoin implements SQLTableName, Aliased + abstract class SQLBaseJoin implements SQLTableName, SQLRealTableName, Aliased { protected $subject = null; protected $alias = null; @@ -35,6 +35,11 @@ public function getTable() return $this->alias ? $this->alias : $this->subject; } + public function getRealTable() + { + return $this->subject; + } + protected function baseToString(Dialect $dialect, $logic = null) { return diff --git a/core/OSQL/SQLRealTableName.class.php b/core/OSQL/SQLRealTableName.class.php new file mode 100644 index 0000000000..c144c2b3b5 --- /dev/null +++ b/core/OSQL/SQLRealTableName.class.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/core/OSQL/SelectQuery.class.php b/core/OSQL/SelectQuery.class.php index c72300b568..5097dd90d8 100644 --- a/core/OSQL/SelectQuery.class.php +++ b/core/OSQL/SelectQuery.class.php @@ -99,6 +99,11 @@ public function hasJoinedTable($table) return $this->joiner->hasJoinedTable($table); } + public function getJoinedTables() + { + return $this->joiner->getTables(); + } + /** * @return SelectQuery **/ diff --git a/main/DAOs/Uncachers/UncacherTaggableDaoWorker.class.php b/main/DAOs/Uncachers/UncacherTaggableDaoWorker.class.php index 1f32c95d49..5729cfc382 100644 --- a/main/DAOs/Uncachers/UncacherTaggableDaoWorker.class.php +++ b/main/DAOs/Uncachers/UncacherTaggableDaoWorker.class.php @@ -19,14 +19,15 @@ class UncacherTaggableDaoWorker implements UncacherBase /** * @return UncacherTaggableDaoWorker */ - public static function create($className, $idKey, $tags, TaggableDaoWorker $worker) + public static function create($className, $idKey, array $tags = array()) { - return new self($className, $idKey, $tags, $worker); + return new self($className, $idKey, $tags); } - public function __construct($className, $idKey, $tags, TaggableDaoWorker $worker) + public function __construct($className, $idKey, array $tags = array()) { - $this->classNameMap[$className] = array(array($idKey), $tags, $worker); + $idKeyList = $idKey ? array($idKey) : array(); + $this->classNameMap[$className] = array($idKeyList, $tags); } /** @@ -49,14 +50,18 @@ public function merge(UncacherBase $uncacher) public function uncache() { foreach ($this->classNameMap as $className => $uncaches) { - list($idKeys, $tags, $worker) = $uncaches; - /* @var $worker TaggableDaoWorker */ + list($idKeys, $tags) = $uncaches; + $dao = ClassUtils::callStaticMethod("$className::dao"); + /* @var $dao StorableDAO */ + $worker = Cache::worker($dao); + Assert::isInstance($worker, 'TaggableDaoWorker'); + $worker->expireTags($tags); foreach ($idKeys as $key) - Cache::me()->mark($className)->delete($idKey); + Cache::me()->mark($className)->delete($key); - ClassUtils::callStaticMethod("$className::dao")->uncacheLists(); + $dao->uncacheLists(); } } diff --git a/main/DAOs/Uncachers/UncacherTaggableDaoWorkerTags.class.php b/main/DAOs/Uncachers/UncacherTaggableDaoWorkerTags.class.php new file mode 100644 index 0000000000..5f8ce03048 --- /dev/null +++ b/main/DAOs/Uncachers/UncacherTaggableDaoWorkerTags.class.php @@ -0,0 +1,76 @@ +classNameMap[$className] = $tags; + } + + /** + * @return array + */ + public function getClassNameMap() + { + return $this->classNameMap; + } + /** + * @param $uncacher UncacherTaggableDaoWorkerTags same as self class + * @return BaseUncacher (this) + */ + public function merge(UncacherBase $uncacher) + { + Assert::isInstance($uncacher, 'UncacherTaggableDaoWorkerTags'); + return $this->mergeSelf($uncacher); + } + + public function uncache() + { + foreach ($this->classNameMap as $className => $tags) { + $dao = ClassUtils::callStaticMethod("$className::dao"); + /* @var $dao StorableDAO */ + $worker = Cache::worker($dao); + Assert::isInstance($worker, 'TaggableDaoWorker'); + + $worker->expireTags($tags); + } + } + + private function mergeSelf(UncacherTaggableDaoWorkerTags $uncacher) { + foreach ($uncacher->getClassNameMap() as $className => $tags) { + if (!isset($this->classNameMap[$className])) { + $this->classNameMap[$className] = $tags; + } else { + //merging idkeys + $this->classNameMap[$className] = ArrayUtils::mergeUnique( + $this->classNameMap[$className], + $tags + ); + } + } + return $this; + } + } +?> \ No newline at end of file diff --git a/main/DAOs/Workers/TaggableDaoWorker.class.php b/main/DAOs/Workers/TaggableDaoWorker.class.php new file mode 100644 index 0000000000..e8838b4a31 --- /dev/null +++ b/main/DAOs/Workers/TaggableDaoWorker.class.php @@ -0,0 +1,445 @@ +updateTags($tags); + + return $this; + } + + /// cachers + //@{ + protected function cacheByQuery( + SelectQuery $query, + /* Identifiable */ $object, + $expires = Cache::EXPIRES_FOREVER + ) + { + $key = $this->makeQueryKey($query, self::SUFFIX_QUERY); + + Cache::me()-> + mark($this->className)-> + set( + $key, + array( + 'tags' => $this->getTagsForQuery($query), + 'data' => $object, + ), + $expires + ); + +// SemaphorePool::me()->free(self::LOCK_PREFIX.$key); + + return $object; + } + + protected function cacheById( + Identifiable $object, $expires = Cache::EXPIRES_FOREVER + ) + { + if ($expires !== Cache::DO_NOT_CACHE) { + + Cache::me()-> + mark($this->className)-> + set( + $this->makeIdKey($object->getId()), + array( + 'tags' => $this->getTagsForObject($object), + 'data' => $object, + ), + $expires + ); + } + + return $object; + } + + protected function cacheListByQuery( + SelectQuery $query, + /* array || Cache::NOT_FOUND */ $array, + $expires = Cache::EXPIRES_FOREVER + ) + { + if ($array !== Cache::NOT_FOUND) { + Assert::isArray($array); + Assert::isTrue(current($array) instanceof Identifiable); + } + + $key = $this->makeQueryKey($query, self::SUFFIX_LIST); + + Cache::me()-> + mark($this->className)-> + set( + $key, + array( + 'tags' => $this->getTagsForQuery($query), + 'data' => $array, + ), + $expires + ); + +// SemaphorePool::me()->free(self::LOCK_PREFIX.$key); + + return $array; + } + + protected function cacheNullById($id, $expires = Cache::EXPIRES_FOREVER) + { + return + Cache::me()-> + mark($this->className)-> + add( + $this->makeIdKey($id), + array( + 'tags' => $this->getTagsForNullObject($id), + 'data' => Cache::NOT_FOUND, + ), + $expires + ); + } + //@} + + /// getters + //@{ + public function getCachedById($id) + { + $result = + Cache::me()-> + mark($this->className)-> + get($this->makeIdKey($id)); + + if ($this->checkValid($result)) + return $result['data']; + + return null; + } + + public function getListByIds(array $ids, $expires = Cache::EXPIRES_FOREVER) + { + $list = array(); + $toFetch = array(); + $prefixed = array(); + + $proto = $this->dao->getProtoClass(); + + $proto->beginPrefetch(); + + // dupes, if any, will be resolved later @ ArrayUtils::regularizeList + $ids = array_unique($ids); + + foreach ($ids as $id) + $prefixed[$id] = $this->makeIdKey($id); + + if ( + $cachedList + = Cache::me()->mark($this->className)->getList($prefixed) + ) { + foreach ($cachedList as $cached) { + if ($this->checkValid($cached)) { + $cached = $cached['data']; + if ($cached && ($cached !== Cache::NOT_FOUND)) { + $list[] = $this->dao->completeObject($cached); + + unset($prefixed[$cached->getId()]); + } + } + } + } + + $toFetch += array_keys($prefixed); + + if ($toFetch) { + $remainList = array(); + + foreach ($toFetch as $id) { + try { + $remainList[] = $this->getById($id); + } catch (ObjectNotFoundException $e) {/*_*/} + } + + $list = array_merge($list, $remainList); + } + + $proto->endPrefetch($list); + + return $list; + } + //@} + + /// uncachers + //@{ + public function uncacheById($id) + { + return $this->registerUncacher($this->getUncacherById($id)); + } + + /** + * @return UncacherBase + */ + public function getUncacherById($id) + { + $className = $this->className; + $idKey = $this->makeIdKey($id); + + try { + $object = $this->dao->getById($id); + $tags = self::$handler->getUncacheObjectTags($object, $className); + } catch (ObjectNotFoundException $e) { + $tags = array(); + } + + return UncacherTaggableDaoWorker::create($className, $idKey, $tags); + } + + public function uncacheByIds($ids) + { + if (empty($ids)) + return; + + $uncacher = $this->getUncacherById(array_shift($ids)); + + foreach ($ids as $id) + $uncacher->merge($this->getUncacherById($id)); + + return $this->registerUncacher($uncacher); + } + + public function uncacheLists() + { + $tags = self::$handler->getDefaultTags($this->className); + return $this->registerUncacher( + UncacherTaggableDaoWorkerTags::create($this->className, $tags) + ); + } + + //@} + + /// internal helpers + //@{ + protected function gentlyGetByKey($key) + { + $result = + Cache::me()->mark($this->className)->get($key); + + if ($this->checkValid($result)) { + return $result['data']; + } + +// $pool = SemaphorePool::me(); +// +// if (!$pool->get(self::LOCK_PREFIX.$key)) { +// if ($result && isset($result['data'])) { +// return $result['data']; +// } else { +// for ($msec = 0; $msec <= self::LOCK_TIMEOUT; $msec += 200) { +// usleep(200*1000); +// if ($pool->get(self::LOCK_PREFIX.$key)) { +// $result = +// Cache::me()->mark($this->className)->get($key); +// +// $pool->free(self::LOCK_PREFIX.$key); +// +// if ($this->checkValid($result)) { +// return $result['data']; +// } else { +// // лока уже нет, а кэш не перестроился +// continue; +// } +// } +// } +// // не дождались снятия лока +// throw new DeadLockException( +// "Cache deadlock. {$this->className} QueryKey={$key}" +// ); +// } +// } + + return null; + } + + protected function checkValid($item) + { + return + $item + && isset($item['data']) + && isset($item['tags']) + && $this->checkTagVersions($item['tags']); + } + + /** + * узнает список тегов которые используются в запросе, + */ + protected function getTagsForQuery(SelectQuery $query) + { + if (self::$customTags) { + $tags = self::$customTags; + } else { + $tags = self::$handler->getQueryTags($query, $this->className); + } + + $tagList = array(); + foreach ($tags as $tag) { + $tagList[$tag] = 0; + } + + return $this->getTagVersions($tagList); + } + + protected function getTagsForNullObject($id) + { + $tags = self::$handler->getNullObjectTags($id, $this->className); + $tagList = array(); + foreach ($tags as $tag) { + $tagList[$tag] = 0; + } + + return $this->getTagVersions($tagList); + } + + protected function getTagVersions(/*array*/ $tags) + { + $time = microtime(true); + $tagsToFetch = array_keys($tags); + + if ( + !$result = + Cache::me()-> + mark(self::TAG_VERSIONS)-> + getList($tagsToFetch) + ) { + $result = array(); + } + + $fetchedTags = array(); + foreach ($tagsToFetch as $tag) { + $fetched = false; + foreach ($result as $key => $value) { + if (strpos($key, $tag) !== false) { + $fetched = true; + $fetchedTags[$tag] = $value; + } + } + if (!$fetched) { + Cache::me()-> + mark(self::TAG_VERSIONS)-> + set( + $tag, + $time, + Cache::EXPIRES_FOREVER + ); + + $fetchedTags[$tag] = $time; + } + } + + return $fetchedTags; + } + + /** + * проверяет версии тегов + */ + protected function checkTagVersions(/*array*/ $tags) + { + $tagVersions = $this->getTagVersions($tags); + if ($tagVersions == $tags) { + return true; + } + + return false; + } + + protected function updateTagVersions(IdentifiableObject $object) + { + $tags = self::$handler->getUncacheObjectTags($object, $this->className); + + $this->updateTags($tags); + + return true; + } + + protected function getTagsForObject(IdentifiableObject $object) + { + $tags = self::$handler->getCacheObjectTags($object, $this->className); + $tagList = array(); + foreach ($tags as $tag) { + $tagList[$tag] = 0; + } + + return $this->getTagVersions($tagList); + } + + protected function updateTags($tags) + { + $time = microtime(true); + foreach ($tags as $tag) { + Cache::me()-> + mark(self::TAG_VERSIONS)-> + set( + $tag, + $time, + Cache::EXPIRES_FOREVER + ); + } + + return true; + } + + protected function makeIdKey($id) + { + return parent::makeIdKey($id).self::KEY_POSTFIX; + } + + protected function makeQueryKey(SelectQuery $query, $suffix) + { + return parent::makeQueryKey($query, $suffix).self::KEY_POSTFIX; + } + //@} + } +?> \ No newline at end of file diff --git a/main/DAOs/Workers/TaggableHandler.class.php b/main/DAOs/Workers/TaggableHandler.class.php new file mode 100644 index 0000000000..02c5ace6ed --- /dev/null +++ b/main/DAOs/Workers/TaggableHandler.class.php @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/main/DAOs/Workers/TaggableLayerHandler.class.php b/main/DAOs/Workers/TaggableLayerHandler.class.php new file mode 100644 index 0000000000..98c21a4542 --- /dev/null +++ b/main/DAOs/Workers/TaggableLayerHandler.class.php @@ -0,0 +1,42 @@ +getDefaultTags($className); + } + + public function getDefaultTags($className) + { + return array($className); + } + } +?> \ No newline at end of file diff --git a/main/DAOs/Workers/TaggableSmartHandler.class.php b/main/DAOs/Workers/TaggableSmartHandler.class.php new file mode 100644 index 0000000000..be72b493dc --- /dev/null +++ b/main/DAOs/Workers/TaggableSmartHandler.class.php @@ -0,0 +1,240 @@ +getTagByClassAndId($className, $object->getId())); + } + + public function getUncacheObjectTags(IdentifiableObject $object, $className) + { + $tags = $this->getCacheObjectTags($object, $className); + $tags = array_merge($this->getDefaultTags($className), $tags); + + foreach ($this->getLinkProperties($className) as $name => $property) { + /* @var $property LightMetaProperty */ + if ($name == 'id') { + continue; + } + if ($property->getClassName()) { + if ($property->getRelationId() == MetaRelation::ONE_TO_ONE) { + if ($property->getFetchStrategyId() == FetchStrategy::LAZY) { + if ($linkedObjectId = $object->{$property->getGetter().'Id'}()) { + $tags[] = $this->getTagByClassAndId($property->getClassName(), $linkedObjectId); + } + } elseif ($property->getFetchStrategyId()) { + if ( + ($linkedObject = $object->{$property->getGetter()}()) + && $linkedObject instanceof IdentifiableObject + && $linkedObjectId = $linkedObject->getId() + ) { + $tags[] = $this->getTagByClassAndId($property->getClassName(), $linkedObjectId); + } + } + } elseif ($property->getRelationId() == MetaRelation::MANY_TO_MANY) { + $daoHelper = $object->{$property->getGetter()}(); + /* @var $daoHelper ManyToManyLinked */ + $tags[] = $daoHelper->getHelperTable(); + } + } + } + + return $tags; + } + + public function getQueryTags(SelectQuery $query, $className) + { + $columns = $this->getLinkObjectColumnListByClass($className); + + $tagList = array(); + if ($query->getTablesCount() > 1 || !$this->isLazy($className)) { + foreach ($query->getJoinedTables() as $table) { + /* @var $table SQLRealTableName */ + $tagList[] = $table->getRealTable(); + } + } else { + foreach ($query->getWhere() as $whereObject) { + if ($whereObject instanceof BinaryExpression) { + if ($tag = $this->getTagByBinaryExpression($whereObject, $query, $className, $columns)) { + $tagList[] = $tag; + } + } + if ($whereObject instanceof LogicalChain) { + foreach ($whereObject->getChain() as $logic) { + if ($logic instanceof BinaryExpression) { + if ($tag = $this->getTagByBinaryExpression($logic, $query, $className, $columns)) { + $tagList[] = $tag; + } + } + } + } + } + + if (empty($tagList)) { + $tagList = $this->getDefaultTags($className); + } + } + + return $tagList; + } + + public function getNullObjectTags($id, $className) + { + $tags = $this->getDefaultTags($className); + $tags[] = $this->getTagByClassAndId($className, $id); + + return $tags; + } + + public function getDefaultTags($className) + { + return array($this->getTableByClassName($className)); + } + + protected function getTagByClassAndId($className, $id) + { + return $this->getTableByClassName($className).self::ID_POSTFIX.$id; + } + + protected final function getTagByBinaryExpression(BinaryExpression $expression, SelectQuery $query, &$className, &$columns) + { + $tag = null; + + if ($expression->getLogic() == BinaryExpression::EQUALS) { + $first = $expression->getLeft(); + $second = $expression->getRight(); + if ($second instanceof DBField || $first instanceof DBValue) { + $first = $expression->getRight(); + $second = $expression->getLeft(); + } + + $columnClassName = null; + $idValue = null; + if ( + $first instanceof DBField + && isset($columns[$first->getField()]) + ) { + if ($first->getTable() === null) { + $table = $query->getFirstTable(); + } elseif ($first->getTable() instanceof FromTable) { + $table = $first->getTable(); + } + if ($table instanceof FromTable) { + $table = $table->getTable(); + } + + if ($table !== null && $table == $this->getTableByClassName($className)) { + $columnClassName = $columns[$first->getField()]; + } + } elseif (is_string($first) && $first && isset($columns[$first])) { + $table = $query->getFirstTable(); + if ($table instanceof FromTable) { + $table = $table->getTable(); + } + if ($table !== null && $table == $this->getTableByClassName($className)) { + $columnClassName = $columns[$first]; + } + } + + if ($second instanceof DBValue) { + $idValue = $second->getValue(); + } elseif ((is_integer($second) || is_string($second)) && $second) { + $idValue = $second; + } + + if ($columnClassName && $idValue) { + $tag = $this->getTagByClassAndId($columnClassName, $idValue); + } + } + + return $tag; + } + + protected function getLinkObjectColumnListByClass($className) + { + static $result = array(); + if (!isset($result[$className])) { + $columnList = array(); + foreach ($this->getLinkProperties($className) as $property) { + /* @var $property LightMetaProperty */ + if ($property->getRelationId() == MetaRelation::ONE_TO_ONE || $property->getName() == 'id') { + $columnList[$property->getColumnName()] = $property->getClassName(); + } + } + $result[$className] = $columnList; + } + return $result[$className]; + } + + protected function getTableByClassName($className) { + if (ClassUtils::isInstanceOf($className, 'DAOConnected')) { + return ClassUtils::callStaticMethod("{$className}::dao")->getTable(); + } else { + return $className.'|className|'; + } + } + + protected function isLazy($className) + { + foreach ($this->getLinkProperties($className) as $property) { + if ( + $property->getRelationId() == MetaRelation::ONE_TO_ONE + && ($property->getFetchStrategyId() == FetchStrategy::CASCADE + || $property->getFetchStrategyId() == FetchStrategy::JOIN) + ) { + return false; + } + } + + return true; + } + + protected function getLinkProperties($className) { + $propertyList = array(); + foreach ($this->getPropertiesByClassName($className) as $name => $property) { + if ($property instanceof InnerMetaProperty) { + $propertyList = array_merge( + $propertyList, + $this->getLinkProperties($property->getClassName()) + ); + } elseif ($property instanceof LightMetaProperty) { + switch ($property->getType()) { + case 'identifier': + case 'identifierList': + case 'integerIdentifier': + case 'integerIdentifierList': + case 'scalarIdentifier': + case 'scalarIdentifierList': + if ($property->getClassName()) { + $propertyList[] = $property; + } + break; + } + } + } + return $propertyList; + } + + protected function getPropertiesByClassName($className) + { + $proto = ClassUtils::callStaticMethod($className.'::proto'); + + return $proto->getPropertyList(); + } + } +?> \ No newline at end of file diff --git a/main/UnifiedContainer/UnifiedContainer.class.php b/main/UnifiedContainer/UnifiedContainer.class.php index ee7d33a5bd..882189dd61 100644 --- a/main/UnifiedContainer/UnifiedContainer.class.php +++ b/main/UnifiedContainer/UnifiedContainer.class.php @@ -330,6 +330,7 @@ public function save() $this->clones = array(); $this->syncClones(); + $this->parent->dao()->uncacheById($this->getParentObject()->getId()); $this->dao->uncacheLists(); return $this; diff --git a/meta/builders/AutoClassBuilder.class.php b/meta/builders/AutoClassBuilder.class.php index f0995ec7bb..656dc5330a 100644 --- a/meta/builders/AutoClassBuilder.class.php +++ b/meta/builders/AutoClassBuilder.class.php @@ -42,13 +42,13 @@ public static function build(MetaClass $class) if (!self::doPropertyBuild($class, $property, $isNamed)) continue; - $out .= - "protected \${$property->getName()} = " - ."{$property->getType()->getDeclaration()};\n"; - if ($property->getFetchStrategyId() == FetchStrategy::LAZY) { $out .= "protected \${$property->getName()}Id = null;\n"; + } else { + $out .= + "protected \${$property->getName()} = " + ."{$property->getType()->getDeclaration()};\n"; } } diff --git a/meta/types/ObjectType.class.php b/meta/types/ObjectType.class.php index fa90a9b6ec..ebbec77327 100644 --- a/meta/types/ObjectType.class.php +++ b/meta/types/ObjectType.class.php @@ -114,18 +114,16 @@ public function {$methodName}() {$classHint} public function {$methodName}() { - if (!\$this->{$name} && \$this->{$name}Id) { - \$this->{$name} = {$fetchObjectString}; + if (\$this->{$name}Id !== null) { + return {$fetchObjectString}; } - return \$this->{$name}; + return null; } public function {$methodName}Id() { - return \$this->{$name} - ? \$this->{$name}->getId() - : \$this->{$name}Id; + return \$this->{$name}Id; } EOT; @@ -229,7 +227,6 @@ public function {$methodName}({$property->getType()->getClassName()} \${$name}) **/ public function {$methodName}({$this->className} \${$name}) { - \$this->{$name} = \${$name}; \$this->{$name}Id = \${$name}->getId(); return \$this; @@ -240,7 +237,6 @@ public function {$methodName}({$this->className} \${$name}) **/ public function {$methodName}Id(\$id) { - \$this->{$name} = null; \$this->{$name}Id = \$id; return \$this; @@ -307,7 +303,6 @@ public function {$methodName}() **/ public function {$methodName}() { - \$this->{$name} = null; \$this->{$name}Id = null; return \$this; diff --git a/test/config.inc.php.tpl b/test/config.inc.php.tpl index 36184d8f65..90497f26e2 100644 --- a/test/config.inc.php.tpl +++ b/test/config.inc.php.tpl @@ -48,10 +48,11 @@ $daoWorkers = array( 'NullDaoWorker', 'CommonDaoWorker', 'SmartDaoWorker', 'VoodooDaoWorker', - 'CacheDaoWorker', 'VoodooDaoWorker', 'SmartDaoWorker', 'CommonDaoWorker', 'NullDaoWorker' + 'CacheDaoWorker', 'TaggableDaoWorker', 'VoodooDaoWorker', 'SmartDaoWorker', 'CommonDaoWorker', 'NullDaoWorker' ); VoodooDaoWorker::setDefaultHandler('CacheSegmentHandler'); + TaggableDaoWorker::setHandler('TaggableSmartHandler'); define('__LOCAL_DEBUG__', true); ?> diff --git a/test/core/TaggableSmartHandlerTest.class.php b/test/core/TaggableSmartHandlerTest.class.php new file mode 100644 index 0000000000..0f03379c5e --- /dev/null +++ b/test/core/TaggableSmartHandlerTest.class.php @@ -0,0 +1,118 @@ +assertEquals( + array(TestLazy::dao()->getTable()), + $this->spawnHandler()->getQueryTags($criteria->toSelectQuery(), 'TestLazy') + ); + } + + public function testTableLazyListById() + { + $criteria = Criteria::create(TestLazy::dao()) + ->add(Expression::eq('id', DBValue::create('33'))); + + $expectTags = array( + TestLazy::dao()->getTable().TaggableSmartHandler::ID_POSTFIX.'33', + ); + $this->assertEquals( + $expectTags, + $this->spawnHandler()->getQueryTags($criteria->toSelectQuery(), 'TestLazy') + ); + } + + public function testTableLazyListByCities() + { + $criteria = Criteria::create(TestLazy::dao()) + ->add(Expression::eq('city', DBValue::create('1'))) + ->add(Expression::eq(DBValue::create('2'), 'cityOptional.id')); + + $expectTags = array( + TestCity::dao()->getTable().TaggableSmartHandler::ID_POSTFIX.'1', + TestCity::dao()->getTable().TaggableSmartHandler::ID_POSTFIX.'2', + ); + $this->assertEquals( + $expectTags, + $this->spawnHandler()->getQueryTags($criteria->toSelectQuery(), 'TestLazy') + ); + } + + public function testTableLazyListByCityName() + { + $criteria = Criteria::create(TestLazy::dao()) + ->add(Expression::eq('city.name', 'Moscow')); + + $expectTags = array( + TestLazy::dao()->getTable(), + TestCity::dao()->getTable(), + ); + $this->assertEquals( + $expectTags, + $this->spawnHandler()->getQueryTags($criteria->toSelectQuery(), 'TestLazy') + ); + } + + public function testTableUserListFetchedWithJoins() + { + $criteria = Criteria::create(TestUser::dao()); + + $expectTags = array( + TestUser::dao()->getTable(), + TestCity::dao()->getTable(), + TestCity::dao()->getTable(), + TestCity::dao()->getTable(), + ); + $this->assertEquals( + $expectTags, + $this->spawnHandler()->getQueryTags($criteria->toSelectQuery(), 'TestUser') + ); + } + + public function testTableLazyThroughValueObject() + { + $criteria = Criteria::create(TestUserWithContact::dao()) + ->add(Expression::eq('contacts.city.id', DBValue::create('3'))); + + $expectTags = array( + TestCity::dao()->getTable().TaggableSmartHandler::ID_POSTFIX.'3', + ); + + $this->assertEquals( + $expectTags, + $this->spawnHandler()->getQueryTags($criteria->toSelectQuery(), 'TestUserWithContact') + ); + + } + + public function testTableLazyListInCities() + { + $this->markTestIncomplete('Not implemented feature, but you can ;)'); + + $criteria = Criteria::create(TestLazy::dao()) + ->add(Expression::in('city', array('1', '2', '42'))); + + $expectTags = array( + TestCity::dao()->getTable().TaggableSmartHandler::ID_POSTFIX.'1', + TestCity::dao()->getTable().TaggableSmartHandler::ID_POSTFIX.'2', + TestCity::dao()->getTable().TaggableSmartHandler::ID_POSTFIX.'42', + ); + $this->assertEquals( + $expectTags, + $this->spawnHandler()->getQueryTags($criteria->toSelectQuery(), 'TestLazy') + ); + } + + /** + * @return TaggableSmartHandler + */ + private function spawnHandler() + { + return new TaggableSmartHandler(); + } + } +?> \ No newline at end of file diff --git a/test/meta/config.meta.xml b/test/meta/config.meta.xml index ba98dc7840..adae22d362 100644 --- a/test/meta/config.meta.xml +++ b/test/meta/config.meta.xml @@ -117,7 +117,7 @@ - + diff --git a/test/misc/DAOTest.class.php b/test/misc/DAOTest.class.php index d19949a603..f078f3b662 100644 --- a/test/misc/DAOTest.class.php +++ b/test/misc/DAOTest.class.php @@ -871,10 +871,9 @@ public function testLazy() $this->create(); $parent = TestParentObject::create(); - $child = TestChildObject::create()->setParent($parent); - $parent->dao()->add($parent); + $child = TestChildObject::create()->setParent($parent); $child->dao()->add($child); $this->assertEquals( diff --git a/test/runme.sh b/test/runme.sh index b3224506df..036b9cf352 100755 --- a/test/runme.sh +++ b/test/runme.sh @@ -3,7 +3,7 @@ cd `dirname $0` if [ "$*" = '' ] then - ARGS='--repeat 9 AllTests' + ARGS='--repeat 10 AllTests' else ARGS="$*" fi