Complex reference maps in Zend_Db_Table to account for multi-column keys
I am going to attempt to keep this as simple as possible, but the use case is outside the original intention of Zend_Db I fear. It concerns a set of tables I have for tagging pages (or anything else eg. documents) in a CMS.
I have three tables:
- Pages (
pages
) - Tags (
tags
) - TagLink (
tags_link
) which is a many-to-many linking table between Pages and Tags
Pages is a simple table (I have removed the inconsequential columns from the code below):
CREATE TABLE `pages` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
FULLTEXT KEY `search` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
Tags is quite simple as well although there is a self-referential column (parent_tag_id
):
CREATE TABLE `tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tag` varchar(255) NOT NULL,
`parent_tag_id` int(11)开发者_JAVA技巧 NOT NULL DEFAULT '0',
`updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `GetByParentTagId` (`parent_tag_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
TagLink is again fairly simple:
CREATE TABLE `tags_link` (
`tag_id` int(11) NOT NULL,
`module_type` varchar(50) NOT NULL,
`foreign_key` int(11) NOT NULL,
UNIQUE KEY `Unique` (`tag_id`,`module_type`,`foreign_key`),
KEY `Search` (`module_type`,`foreign_key`),
KEY `AllByTagId` (`tag_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
The complicating factor is that TagLink is able to link against any other table in the database and not just Pages. So if for example I had a documents upload section then that could also be tagged. To facilitate this way of working there is effectively a multi-column key.
To make this clearer I will demonstrate a couple of insert queries that might be run when tags are added to a table (eg. Pages):
INSERT INTO `tags_link`
SET `tag_id` = '1',
`module_type` = 'Pages',
`foreign_key` = '2'
INSERT INTO `tags_link`
SET `tag_id` = '1',
`module_type` = 'Documents',
`foreign_key` = '3'
So as you can see the module_type
column is simply an arbitrary string that describes where the foreign key can be found. This is not the name of the table however as anything with an ID can have tags linked to it even if it is not necessarily in the MySQL database.
Now to the Zend_Db_Table
$_referenceMap
in PageTable
:
protected $_referenceMap = array(
'TagLink' => array(
'columns' => 'id',
'refTableClass' => 'Models_Tag_TagLinkTable',
'refColumns' => 'foreign_key'
),
);
But this does not take into account my arbitrary module_type
column and will return any TagLink with the same foreign key. Obviously this is bad because you get TagLinks for documents mixed in with those for pages for instance.
So my question is how can I take into account this additional column when setting up this reference? The aim is to avoid having a TagLink class for each module_type
as I have now.
I would imagine something like the following could explain my requirements although obviously this is not how it would be done:
protected $_referenceMap = array(
'TagLink' => array(
'columns' => 'id',
'refTableClass' => 'Models_Tag_TagLinkTable',
'refColumns' => 'foreign_key',
'where' => 'module_type = "Pages"'
),
);
My current implementation overrides the _fetch
method in the Documents_TagLinkTable
in the following way:
protected function _fetch(Zend_Db_Table_Select $select) {
$select->where("module_type = 'Documents_Secondary_Tags' OR module_type = 'Documents_Primary_Tags' OR module_type = 'Documents'");
return parent::_fetch($select);
}
As you can see there maybe more than one set of tags added to any object as well.
Example 3 in "Fetching Dependent Rowsets" in the Zend Framework reference demonstrates a technique you could use:
http://framework.zend.com/manual/en/zend.db.table.relationships.html
Whilst it doesnt show a "where" clause being included in the select, it should work.
Duncan
精彩评论