我有以下两个表:
CREATE TABLE IF NOT EXISTS `skills` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`skill_category` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`skill` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`icon_filename` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`display_priority` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `skill_category` (`skill_category`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `skills_categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`skill_category` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`display_priority` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `IDX_95FF1C8D47E90E27` (`skill_category`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
ALTER TABLE `skills`
ADD CONSTRAINT `skills_ibfk_1` FOREIGN KEY (`skill_category`) REFERENCES `skills_categories` (`skill_category`);
注意两个skill_category列之间的外键关系。
我正在尝试在这些表之间创建多个/ onetomany关系。一个独特的skill_category应该有很多技能。这个结构似乎工作正常,除非我尝试验证映射时得到错误:
引用的列名'skill_category'必须是主键 目标实体类的列 '恢复\ ResumeBundle \实体\ SkillsCategories'。
学说要求引用的列是主键。如果我将此键设为主键,则我的id列不再自动递增,这是我想要的。因此,对于外键是主键,mySQL似乎没问题,但是Doctrine抱怨这种情况。有人说他们只是通过重新创建列来修复它:
我试过这个,但对我来说似乎并没有帮助。所以要么这是某种错误,要么我对RDBMS有一个基本的误解(完全可能)。我的问题是我不应该使用'id'列吗?如果我希望我的'skills_category'列是唯一的,我应该把它作为主键并删除'id'列?这样可以解决我的问题,但是将VARCHAR作为主键有什么问题吗?谷歌的答案似乎是“不是真的”,但我会很感激别人的观点。
答案 0 :(得分:2)
这与我第一次开始使用Doctrine时遇到的问题完全相同,是的,它来自对Doctrine如何将对象映射到关系数据库的轻微误解。
让我们从走出“关系”世界开始,把你的实体当作对象来对待。在您的情况下,这里有两类对象:您有一个技能,并且您有一个类别。
这是唯一的两个对象。没有SkillsCategories实体......它作为一个对象没有意义 - 它是一种关系。因此,给它一个ID并不是很重要。
那么这两个实体究竟是什么样的呢?
技能:
类别:
当你将这些实体放在数据库中时,它看起来就像你期望的那样(除了我们没有映射属于类别的技能列表 - 稍后将由Doctrine处理)。我建议您更新架构以反映此更改:
CREATE TABLE IF NOT EXISTS `Skill` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`categoryId` int(11) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`iconFilename` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`displayPriority` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `skill_category` (`categoryId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `Category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`displayPriority` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `IDX_95FF1C8D47E90E27` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
现在您可以通过Doctrine定义关系。以下是YAML的样子:
Path\To\Entity\Skill:
type: entity
table: Skill
fields:
id:
id: true
type: integer
generator
strategy: AUTO
name:
type: string
length: 255
iconFilename:
type: string
length: 50
displayPriority:
type: integer
manyToOne:
category:
targetEntity: Category
inversedBy: skills
joinColumn:
name: categoryId
referencedColumnName: id
Path\To\Entity\Category:
type: entity
table: Category
fields:
id:
id: true
type: integer
generator
strategy: AUTO
name:
type: string
length: 255
displayPriority:
type: integer
oneToMany:
skills:
targetEntity: Skill
mappedBy: category
现在你有一个完全正常工作的模型!
一些例子......
假设您想获得技能类别的displayPriority:
$skill->getCategory()->getDisplayPriority();
...或者如果您想获得给定类别下的技能名称列表:
foreach ($category->getSkills() as $skill) {
echo $skill->getName();
}
希望这有助于澄清一点......
答案 1 :(得分:0)
将varchar作为主键是没有问题的。如果您确实有空间或密钥大小要求,可以设置查找代码表,但这会使您的数据库和代码更加复杂。
我个人使用自动增量键仅用于来自我无法控制的进程的输入:订单,票证,事件等。通常情况下,您正在从Web进程进行跟踪。