I think the best way would be to create some intermediary tables to hold the many:many information, for example;
CREATE TABLE `categories_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_categories` int(11) NOT NULL,
`id_tags` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
ALTER TABLE `categories_tags`
ADD CONSTRAINT `categories_tags_ibfk_1` FOREIGN KEY (`id_categories`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `categories_tags_ibfk_2` FOREIGN KEY (`id_tags`) REFERENCES `tags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
and...
CREATE TABLE `tags_urls` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_tags` int(11) NOT NULL,
`id_urls` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
ALTER TABLE `tags_urls`
ADD CONSTRAINT `tags_urls_ibfk_1` FOREIGN KEY (`id_tags`) REFERENCES `tags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `tags_urls_ibfk_2` FOREIGN KEY (`id_urls`) REFERENCES `urls` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
You can use through in your relations between those tables.
In your category model:
public function relations()
{
return array(
'categoryTags' => array(self::_MANY, 'CategoriesTags', 'id_categories'),
'tags' => array(self::HAS_MANY, 'Tag', 'id_tags', 'through'=>'categoryTags'),
'tagsUrls' => array(self::HAS_MANY, 'TagUrl', 'id_tags', 'through'=>'tags'),
'urls' => array(self::HAS_MANY, 'Url', 'id_url', 'through'=>'tagsUrls'),
);
}
To call all your urls for a given category then you should be able to:
$model = Category::model()->findByPk($categoryId);
$model->urls; // will be an array of url models that belong to the category.
[EDIT] Added the edits above after re-reading the question, I've not tested and you may well need to make a few (or many) edits to the code, but the principle should work in theory. There may well be a quicker way to grab the 'through' relations data, but I've not actually used through in two many:many relationships before, so not had need to try it out myself.