/**
  * saveAnswer
  * save the answer data
  *
  * @param array $questionnaire questionnaire data
  * @param int $userId user id
  * @param string $sessionId session id
  * @param array $data Postされた回答データ
  * @param array &$errors error messages
  * @throws $ex
  * @return bool
  */
 public function saveAnswer($questionnaire, $userId, $sessionId, $data, &$errors)
 {
     $errors = array();
     $this->loadModels(['QuestionnaireAnswerSummary' => 'Questionnaires.QuestionnaireAnswerSummary']);
     //トランザクションBegin
     $this->setDataSource('master');
     //$dataSource = $this->getDataSource();
     //$dataSource->begin();
     try {
         // 初回回答か再回答かを確認している
         // 初めは「ID」がPOSTに入っているかどうかで判断しようと思っていたが
         // Cakeは簡単にブラウザの「戻る」で前画面を表示させたりするので、
         // POSTのIDは再回答であるにもかかわらず空ってことがありうる
         // なので毎回DBチェックするしかないかと思う I think so.
         // サマリレコード取得
         $summary = $this->QuestionnaireAnswerSummary->forceGetAnswerSummary($questionnaire, $userId, $sessionId, array('questionnaire_origin_id' => $questionnaire['Questionnaire']['origin_id'], 'answer_status' => QuestionnairesComponent::ACTION_NOT_ACT, 'session_value' => $sessionId, 'user_id' => $userId));
         $summaryId = $summary['QuestionnaireAnswerSummary']['id'];
         //
         foreach ($data as $answer) {
             // 質問によってバリデーション動作が変わるので
             $targetQuestion = Hash::extract($questionnaire['QuestionnairePage'], '{n}.QuestionnaireQuestion.{n}[origin_id=' . $answer['questionnaire_question_origin_id'] . ']');
             $this->validator()->getField('answer_value')->setRule('answerValidation', array('rule' => array('checkAnswerValue', $targetQuestion[0], $answer, 'message' => '')));
             // データ保存
             if (QuestionnairesComponent::isMatrixInputType($targetQuestion[0]['question_type'])) {
                 foreach ($answer as $ans) {
                     if (!$this->__saveAnswer($ans, $summaryId, $summary)) {
                         $errors[$ans['questionnaire_question_origin_id']] = $this->validationErrors;
                     }
                 }
             } else {
                 if (!$this->__saveAnswer($answer, $summaryId, $summary)) {
                     $errors[$answer['questionnaire_question_origin_id']] = $this->validationErrors;
                 }
             }
         }
         //$dataSource->commit();
     } catch (Exception $ex) {
         //$dataSource->rollback();
         CakeLog::error($ex);
         throw $ex;
     }
     if (count($errors) == 0) {
         return true;
     } else {
         return false;
     }
 }
 /**
  * __aggrigateAnswer
  * 集計処理の実施
  *
  * @param array $questionnaire アンケート情報
  * @param bool $contentEditable 編集可能フラグ
  * @param array &$questions アンケート質問(集計結果を配列追加して返します)
  * @return void
  */
 private function __aggrigateAnswer($questionnaire, $contentEditable, &$questions)
 {
     // 集計データを集める際の基本条件
     $baseConditions = $this->QuestionnaireAnswerSummary->getResultCondition($questionnaire);
     //質問毎に、まとめあげる.
     //$questionsは、questionnaire_question_origin_idをキーとし、questionnaire_question配下が代入されている。
     //
     foreach ($questions as &$question) {
         if ($question['is_result_display'] != QuestionnairesComponent::EXPRESSION_SHOW) {
             //集計表示をしない、なので飛ばす
             continue;
         }
         // 戻り値の、この質問の合計回答数を記録しておく。
         // skip ロジックがあるため、単純にsummaryのcountじゃない..
         $questionConditions = $baseConditions + array('QuestionnaireAnswer.questionnaire_question_origin_id' => $question['origin_id']);
         $question['answer_total_cnt'] = $this->QuestionnaireAnswer->getAnswerCount($questionConditions);
         if (QuestionnairesComponent::isMatrixInputType($question['question_type'])) {
             $this->__aggrigateAnswerForMatrix($question, $questionConditions);
         } else {
             $this->__aggrigateAnswerForNotMatrix($question, $questionConditions);
         }
     }
 }
 /**
  * __getMatrixAns
  *
  * @param array $question question data
  * @param array $choice question choice data
  * @param array $answers answer data
  * @return string
  */
 private function __getMatrixAns($question, $choice, $answers)
 {
     $retAns = '';
     // 回答配列データの中から、現在指定された質問に該当するものを取り出す
     // マトリクスタイプのときは複数存在する(行数分)
     $anss = Hash::extract($answers, '{n}[questionnaire_question_origin_id=' . $question['origin_id'] . ']');
     if (empty($anss)) {
         return $retAns;
     }
     // その中かから現在指定された選択肢行に該当するものを取り出す
     $ans = Hash::extract($anss, '{n}[matrix_choice_id=' . $choice['origin_id'] . ']');
     // 回答が存在するとき処理
     if ($ans) {
         $ans = $ans[0];
         // 選択肢タイプのときは、回答データに選択肢IDとか、複数選ばれていたりとかあるので加工が必要
         // 回答を取り出す
         // choice_id と choice_valueに分けられた回答選択肢配列を得る
         $dividedAnsArray = QuestionnairesComponent::getChoiceValueFromAnswerForSelection($ans['answer_value']);
         // 回答値の先頭に行側の値を入れる。
         // idから判断して、その他が選ばれていた場合、other_answer_valueを入れる
         if ($choice['other_choice_type'] != QuestionnairesComponent::OTHER_CHOICE_TYPE_NO_OTHER_FILED) {
             $retAns = $ans['other_answer_value'] . QuestionnairesComponent::ANSWER_VALUE_DELIMITER;
         } else {
             $retAns = $choice['choice_label'] . QuestionnairesComponent::ANSWER_VALUE_DELIMITER;
         }
         // 選択されていた数分処理
         foreach ($dividedAnsArray as $dividedAns) {
             if (isset($dividedAns[1])) {
                 $retAns .= $dividedAns[1];
             } else {
                 $retAns .= '';
             }
             $retAns .= QuestionnairesComponent::ANSWER_DELIMITER;
         }
         $retAns = trim($retAns, QuestionnairesComponent::ANSWER_DELIMITER);
     }
     return $retAns;
 }