经过一些ORM框架研究,我决定第一次使用螺旋桨。一切都运作良好,直到我开始为缓存机制开展多对多关系。在努力获取插入多个缓存标记的缓存条目(使用事务,而不是手动持久保存所有对象,...)之后,我遇到了通过给定缓存标记查询缓存条目的问题。使用filterByTag(导致使用useTagQuery等)总是以异常结束
Cannot fetch ColumnMap for undefined column: cache_id
引起一段代码:
/**
* Drops all cache entries which are associated to the given tag
*
* @param $tag string The tag to drop
* @return void
*/
public function dropTag($tag) {
$cTag = CacheTagQuery::create()->findOneByTag($tag);
if($cTag instanceof CacheTag) {
$cEntries = CacheQuery::create()->filterByTag($cTag)->find();
foreach($cEntries as $cEntry) {
if($cEntry instanceof Cache) {
$cEntry->delete();
}
}
}
}
schema.xml的相关部分:
<table name="tag">
<column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
<column name="tag" type="VARCHAR" size="255" />
<column name="type" type="INTEGER" inheritance="single">
<inheritance key="1" class="Tag"/>
<inheritance key="2" class="CacheTag" extends="Tag"/>
</column>
<behavior name="cachedrop" />
</table>
<table name="cache">
<column name="id" type="INTEGER" required="true" primaryKey="true" autoIncrement="true" />
<column name="crdate" type="TIMESTAMP" />
<behavior name="timestampable">
<parameter name="create_column" value="crdate" />
<parameter name="disable_updated_at" value="true" />
</behavior>
<column name="lifetime" type="INTEGER" defaultValue="-1" />
<column name="name" type="VARCHAR" />
<column name="content" type="CLOB" size="4294967296" required="true" />
</table>
<table name="cache_tag_mm" isCrossRef="true">
<column name="cache_id" type="INTEGER" primaryKey="true" />
<column name="tag_id" type="INTEGER" primaryKey="true" />
<foreign-key foreignTable="cache">
<reference local="cache_id" foreign="id"/>
</foreign-key>
<foreign-key foreignTable="tag">
<reference local="tag_id" foreign="id"/>
</foreign-key>
</table>
对于插入,我必须在持久化缓存条目之前手动持久化每个标记:
try {
// create our new cache entry
$cEntry = new Cache();
$cEntry->setContent($content);
$cEntry->setLifetime($lifetime);
$cEntry->setName($cachetag);
if(count($processedTags) > 0) {
foreach($processedTags as $pTag) {
$cTag = CacheTagQuery::create()->filterByTag($pTag)->findOneOrCreate();
if($cTag->isNew()) {
$cTag->save();
}
$cEntry->addTag($cTag);
}
}
// finally persist the entry
$cEntry->save();
} catch(Exception $e) {
Logger::debugLog($e->getMessage());
}
开发环境:
PHP 5.3.15与Suhosin-Patch(cli)(内置:2012年8月24日17:45:44) 版权所有(c)1997-2012 PHP小组 Zend Engine v2.3.0,版权所有(c)1998-2012 Zend Technologies Zend Technologies的Zend Debugger v5.2版权所有(c)1999-2009
Apache / 2.2.22(Unix)
即使推进主页上的多对多示例(http://propelorm.org/documentation/04-relationships.html#manytomany_relationships)也会导致此问题。
TagTableMap.php
class TagTableMap extends TableMap
{
/**
* The (dot-path) name of this class
*/
const CLASS_NAME = 'orm.map.TagTableMap';
/**
* Initialize the table attributes, columns and validators
* Relations are not initialized by this method since they are lazy loaded
*
* @return void
* @throws PropelException
*/
public function initialize()
{
// attributes
$this->setName('tag');
$this->setPhpName('Tag');
$this->setClassname('Tag');
$this->setPackage('orm');
$this->setUseIdGenerator(true);
$this->setSingleTableInheritance(true);
// columns
$this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null);
$this->addColumn('TAG', 'Tag', 'VARCHAR', false, 255, null);
$this->addColumn('TYPE', 'Type', 'INTEGER', false, null, null);
// validators
} // initialize()
/**
* Build the RelationMap objects for this table relationships
*/
public function buildRelations()
{
$this->addRelation('CacheTagMm', 'CacheTagMm', RelationMap::ONE_TO_MANY, array('id' => 'tag_id', ), null, null, 'CacheTagMms');
$this->addRelation('Cache', 'Cache', RelationMap::MANY_TO_MANY, array(), null, null, 'Caches');
} // buildRelations()
/**
*
* Gets the list of behaviors registered for this table
*
* @return array Associative array (name => parameters) of behaviors
*/
public function getBehaviors()
{
return array(
'cachedrop' => array(),
);
} // getBehaviors()
} // TagTableMap
CacheTableMap.php
class CacheTableMap extends TableMap
{
/**
* The (dot-path) name of this class
*/
const CLASS_NAME = 'orm.map.CacheTableMap';
/**
* Initialize the table attributes, columns and validators
* Relations are not initialized by this method since they are lazy loaded
*
* @return void
* @throws PropelException
*/
public function initialize()
{
// attributes
$this->setName('cache');
$this->setPhpName('Cache');
$this->setClassname('Cache');
$this->setPackage('orm');
$this->setUseIdGenerator(true);
// columns
$this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null);
$this->addColumn('CRDATE', 'Crdate', 'TIMESTAMP', false, null, null);
$this->addColumn('LIFETIME', 'Lifetime', 'INTEGER', false, null, -1);
$this->addColumn('NAME', 'Name', 'VARCHAR', false, 255, null);
$this->addColumn('CONTENT', 'Content', 'CLOB', true, 4294967296, null);
// validators
} // initialize()
/**
* Build the RelationMap objects for this table relationships
*/
public function buildRelations()
{
$this->addRelation('CacheTagMm', 'CacheTagMm', RelationMap::ONE_TO_MANY, array('id' => 'cache_id', ), null, null, 'CacheTagMms');
$this->addRelation('Tag', 'Tag', RelationMap::MANY_TO_MANY, array(), null, null, 'Tags');
} // buildRelations()
/**
*
* Gets the list of behaviors registered for this table
*
* @return array Associative array (name => parameters) of behaviors
*/
public function getBehaviors()
{
return array(
'timestampable' => array('create_column' => 'crdate', 'update_column' => 'updated_at', 'disable_updated_at' => 'true', ),
);
} // getBehaviors()
} // CacheTableMap
CacheTagMmTableMap.php
class CacheTagMmTableMap extends TableMap
{
/**
* The (dot-path) name of this class
*/
const CLASS_NAME = 'orm.map.CacheTagMmTableMap';
/**
* Initialize the table attributes, columns and validators
* Relations are not initialized by this method since they are lazy loaded
*
* @return void
* @throws PropelException
*/
public function initialize()
{
// attributes
$this->setName('cache_tag_mm');
$this->setPhpName('CacheTagMm');
$this->setClassname('CacheTagMm');
$this->setPackage('orm');
$this->setUseIdGenerator(true);
$this->setIsCrossRef(true);
// columns
$this->addPrimaryKey('ID', 'Id', 'INTEGER', true, null, null);
$this->addForeignKey('CACHE_ID', 'CacheId', 'INTEGER', 'cache', 'ID', false, null, null);
$this->addForeignKey('TAG_ID', 'TagId', 'INTEGER', 'tag', 'ID', false, null, null);
// validators
} // initialize()
/**
* Build the RelationMap objects for this table relationships
*/
public function buildRelations()
{
$this->addRelation('Cache', 'Cache', RelationMap::MANY_TO_ONE, array('cache_id' => 'id', ), null, null);
$this->addRelation('Tag', 'Tag', RelationMap::MANY_TO_ONE, array('tag_id' => 'id', ), null, null);
} // buildRelations()
} // CacheTagMmTableMap
异常回溯:
13 TableMap::getColumn("cache_id")
12 TableMap::addRelation("CacheTagMm", "CacheTagMm", 2, array, NULL, NULL, "CacheTagMms")
11 CacheTableMap::buildRelations()
10 TableMap::getRelations()
9 TableMap::getRelation("CacheTagMm")
8 BaseCacheQuery::joinCacheTagMm(NULL, "LEFT JOIN")
7 BaseCacheQuery::useCacheTagMmQuery()
进一步深入研究TableMaps我发现每个(Tag,Cache,CacheTagMm)构建的buildRelations()函数都包含错误的代码。我通过在作为第4个参数传递的列映射数组上执行strtoupper()来编辑这些部分。 CacheTableMap示例:
public function buildRelations()
{
$this->addRelation('Cache', 'Cache', RelationMap::MANY_TO_ONE, array('CACHE_ID' => 'ID', ), null, null);
$this->addRelation('Tag', 'Tag', RelationMap::MANY_TO_ONE, array('TAG_ID' => 'ID', ), null, null);
} // buildRelations()
这解决了这个问题!但是,我不明白为什么推进构建这样的文件。我的schema.xml中有错误吗?默认命名方法有些问题? 唯一的问题是我每次编辑模式并重建项目时都必须更新这些文件。我把phing版本降级到2.4.5(如文档中的min版本所述),但这并没有改变任何东西。 任何提示?
答案 0 :(得分:1)
我真的建议不要在推进时改变生成的文件,否则当你在架构中更改某些内容时,你会感到非常头痛。对表名的更改是自动推进的,但除非您最初在生成的文本中更改了某些内容,否则列映射不会抛出错误,并且当您构建推进时它应该抛出错误如果您定义的关系存在问题。
您可能想要尝试生成一个新的Propel目录,以及新生成的文件并将它们放在您拥有的文件上,然后查看您是否仍然得到关系错误。只需做一个非常简单的查询,如
$collection = CacheQuery()::create()
->limit(1)
->findOne();
$relationshipTest = $collection->getTag();
看看你是否还有错误。一旦你开始在生成的文件中重命名东西,你就会变得很糟糕。
如果你想覆盖propel的默认命名约定,你可以在你的模式中使用phpname属性,这将用你喜欢的任何方式覆盖propel的默认值(如果你想保留你的下划线)