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 作为选项,因为它会影响整个查询(不仅限制 DayTraining 也是)。

有几种方法可以解决您的问题。最简单的就是定义你的关联为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?