Модели CakePHP 2 hasMany as hasOne

Я пытаюсь сделать это как MVC / CakePHP 2, насколько это возможно, поэтому, если мой подход неверен, я хотел бы знать (все еще учусь). Я чувствую, что то, что я делаю, должно происходить в модели и в меньшей степени в контроллере (чтобы следовать принципам тонкого контроллера толстой модели).

У меня есть отношения hasMany между двумя таблицами:

trainings hasMany days.

Если мне нужны все дни обучения, эта настройка работает, как и ожидалось.

Но я хочу (в каждом случае тренировки) первый день тренировки. Мой мыслительный процесс заключался в том, чтобы установить отношение hasOne в модели Training следующим образом:

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, но я полагаю, что это не имеет значения, поскольку это верное утверждение, которое ничего не ограничивает - просто кажется ненужным приподнятием.


person quijote    schedule 19.12.2015    source источник


Ответы (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
    )
);

При желании вы можете избавиться от дополнительного числового индекса [0], используя Hash::map() после find():

$this->Training->contain('FirstDay');
$trainings=$this->Training->find('all');

$trainings = Hash::map($trainings, "{n}", function($arr){
        $arr['FirstDay']=$arr['FirstDay'][0];
        return $arr;
    });

Другие возможные варианты см.

person Inigo Flores    schedule 20.12.2015
comment
так как это только один, может быть, проще просто сделать: $this->Training->contain('FirstDay'); $trainings=$this->Training->find('all'); $trainings = $trainings[0]; или, может быть, $trainings=$this->Training->find('first'); - person Lucho; 20.12.2015
comment
@lucho Я думаю, вы упустили суть. OP необходимо получить один или несколько Training, но один Day. - person Inigo Flores; 20.12.2015
comment
Клянусь, я пробовал это... Похоже, это сработает. Спасибо Иниго! Хороший маленький трюк, чтобы очистить этот индекс. Я еще не знаю, как я к этому отношусь. - person quijote; 22.12.2015