CakePHP 2 模型 hasMany 和 hasOne
CakePHP 2 Models hasMany as hasOne
我正在尝试尽可能以 MVC / CakePHP 2 的形式执行此操作,所以如果我的方法不正确,我很想知道(仍在学习)。我觉得我正在做的事情应该发生在模型中而不是控制器中(遵循肥胖模型瘦控制器原则)。
我在两个表之间有一个 hasMany 关系:
trainings hasMany days.
如果我希望所有天都在训练中,此设置会按预期工作。
但我希望(在每次培训中)培训的第一天。我的想法是在 Training
模型中设置一个 hasOne 关系,如下所示:
public $hasOne = array(
...
'FirstDay' => array(
'className' => 'Day',
'foreignKey' => 'training_id',
'fields' => 'FirstDay.training_date',
'order' => array('FirstDay.training_date ASC'),
)
);
本质上训练有几天 FirstDay
。
我假设使用此设置,如果我调用 Training
对象,我将获得关联的 FirstDay
。
相反,我得到了 Training
的多个条目 - 一个条目对应给定训练的每个天数实例。得到输出的SQL如下:
SELECT `Training`.`id`, `Training`.`course_id`, `Course`.`name`, ... `FirstDay`.`training_date`
FROM `tst`.`trainings` AS `Training`
LEFT JOIN `tst`.`courses` AS `Course` ON (`Training`.`course_id` = `Course`.`id`)
...
shortened for your benefit
...
LEFT JOIN `tst`.`days` AS `FirstDay` ON (`FirstDay`.`training_id` = `Training`.`id`)
WHERE 1 = 1 ORDER BY `FirstDay`.`training_date` ASC LIMIT 20
我假设 hasOne 会在上面的子句中将限制设置为 1 而不是 20。由于它没有,我尝试添加 'limit' => 1
但这没有用,文档没有提到它作为 hasOne 关系中的一个选项。我也不明白为什么 WHERE 1 = 1
在那里,但我认为这无关紧要,因为它是一个真实的陈述,不限制任何东西——看起来像是不必要的提升。
关系类型 hasOne
是用 LEFT JOIN
实现的,因此不能支持 LIMIT
作为选项,因为它会影响整个查询(不仅限制 Day
但 Training
也是)。
有几种方法可以解决您的问题。最简单的就是定义你的关联为hasMany
,但是设置'limit'=>1
.
public $hasMany = array(
'FirstDay' => array(
'className' => 'Day',
'foreignKey' => 'training_id',
'fields' => 'FirstDay.training_date',
'order' => array('FirstDay.training_date ASC'),
'limit' => 1
)
);
然后,您可以选择在 find()
:
之后使用 Hash::map()
来删除额外的数字索引 [0]
$this->Training->contain('FirstDay');
$trainings=$this->Training->find('all');
$trainings = Hash::map($trainings, "{n}", function($arr){
$arr['FirstDay']=$arr['FirstDay'][0];
return $arr;
});
有关其他可能的选项,请参阅:
- Last x blog entries - but only once per user
- Method for defining simultaneous has-many and has-one associations between two models in CakePHP?
我正在尝试尽可能以 MVC / CakePHP 2 的形式执行此操作,所以如果我的方法不正确,我很想知道(仍在学习)。我觉得我正在做的事情应该发生在模型中而不是控制器中(遵循肥胖模型瘦控制器原则)。
我在两个表之间有一个 hasMany 关系:
trainings hasMany days.
如果我希望所有天都在训练中,此设置会按预期工作。
但我希望(在每次培训中)培训的第一天。我的想法是在 Training
模型中设置一个 hasOne 关系,如下所示:
public $hasOne = array(
...
'FirstDay' => array(
'className' => 'Day',
'foreignKey' => 'training_id',
'fields' => 'FirstDay.training_date',
'order' => array('FirstDay.training_date ASC'),
)
);
本质上训练有几天 FirstDay
。
我假设使用此设置,如果我调用 Training
对象,我将获得关联的 FirstDay
。
相反,我得到了 Training
的多个条目 - 一个条目对应给定训练的每个天数实例。得到输出的SQL如下:
SELECT `Training`.`id`, `Training`.`course_id`, `Course`.`name`, ... `FirstDay`.`training_date`
FROM `tst`.`trainings` AS `Training`
LEFT JOIN `tst`.`courses` AS `Course` ON (`Training`.`course_id` = `Course`.`id`)
...
shortened for your benefit
...
LEFT JOIN `tst`.`days` AS `FirstDay` ON (`FirstDay`.`training_id` = `Training`.`id`)
WHERE 1 = 1 ORDER BY `FirstDay`.`training_date` ASC LIMIT 20
我假设 hasOne 会在上面的子句中将限制设置为 1 而不是 20。由于它没有,我尝试添加 'limit' => 1
但这没有用,文档没有提到它作为 hasOne 关系中的一个选项。我也不明白为什么 WHERE 1 = 1
在那里,但我认为这无关紧要,因为它是一个真实的陈述,不限制任何东西——看起来像是不必要的提升。
关系类型 hasOne
是用 LEFT JOIN
实现的,因此不能支持 LIMIT
作为选项,因为它会影响整个查询(不仅限制 Day
但 Training
也是)。
有几种方法可以解决您的问题。最简单的就是定义你的关联为hasMany
,但是设置'limit'=>1
.
public $hasMany = array(
'FirstDay' => array(
'className' => 'Day',
'foreignKey' => 'training_id',
'fields' => 'FirstDay.training_date',
'order' => array('FirstDay.training_date ASC'),
'limit' => 1
)
);
然后,您可以选择在 find()
:
Hash::map()
来删除额外的数字索引 [0]
$this->Training->contain('FirstDay');
$trainings=$this->Training->find('all');
$trainings = Hash::map($trainings, "{n}", function($arr){
$arr['FirstDay']=$arr['FirstDay'][0];
return $arr;
});
有关其他可能的选项,请参阅:
- Last x blog entries - but only once per user
- Method for defining simultaneous has-many and has-one associations between two models in CakePHP?