PHP javaScript codeIgniter

CSV機能

CSV機能、自作のバリデーションによる複数回のエラー表示、トランザクション処理

バリデーション基本

//バリデーションチェック及びかかった場合のリダイレクト処理を記述
		$error_messages = array();
		$alert_message = '';

		$error_messages = $this->csvValidation($csv_file, $student_list);
		if (!empty($error_messages)) {
			$this->session->set_flashdata('error_messages', $error_messages);
			redirect('/admin/grade/grade_setting_system/rank_aggregate_group/student/csv/register/' . $hr_grade . '/' . $grade_calc_group_id);
		}

// リダイレクト後にフラッシュデータを変数に詰めてビューに渡す
$csv_message = array();
		if (!empty($this->session->userdata('error_messages'))) {
			$csv_message += $this->session->userdata('error_messages');
		}
		if (!empty($this->session->userdata('alert_message'))) {
			$csv_message[] = $this->session->userdata('alert_message');
		}

フロント

<style type="text/css">
	.btnbox button {
		height: auto;
		width: 200px;
		padding: 10px;
	}

	.btnbox {
		padding-left: 50px;
		text-align: left;
		margin-bottom: 3rem;
	}

	p {
		margin-bottom: 20px;
	}

	.info-alert {
		padding: 0.5rem 0;
		color: #FF0000;
	}

	.option-input {
		border: 1px solid #d0d0d0;
		height: 100%;
		border-radius: 5px;
		padding: 10px;
	}

	.option-text {
		width: 200px !important;
	}

	.option-number {
		width: 100px !important;
	}

	.a_button {
		margin-right: 30px;
		background-color: #CCC;
		color: #FFF;
		height: auto;
		width: 200px !important;
		padding: 13px 80px;
		color: #fff;
		letter-spacing: 0.1em;
		-webkit-border-radius: 3px;
		-moz-order-radius: 3px;
		border-radius: 3px;
		border: 0;
		width: 100%;
		font-size: 14px;
		font-weight: 500;
		-webkit-transition: all .15s ease;
		-moz-transition: all .15s ease;
		-ms-transition: all .15s ease;
		-o-transition: all .15s ease;
		transition: all .15s ease;
		min-height: 43px;
	}

	.msg2 {
		font-weight: normal;
	}

	.nice-select {
		z-index: auto;
	}
</style>

<?php

use domain\model\GradeEvaluateFrameOption\m_RegistType;
use domain\model\GradeEvaluateFrameOption\m_TimingType;
?>

<form name="csv_export" method="POST" action="/admin/grade/grade_setting_system/rank_aggregate_group/student/csv/export/<?= $hr_grade; ?>/<?= $grade_calc_group_id ?>" enctype="multipart/form-data">
</form>

<form name="csv_import" method="POST" action="/admin/grade/grade_setting_system/rank_aggregate_group/student/csv/import/<?= $hr_grade; ?>/<?= $grade_calc_group_id ?>" enctype="multipart/form-data">
	<input type="file" name="csv_file" id="csv_file" accept=".csv,text/csv" style="display:none">
</form>

<div>
	<h1>学年別順位集計グループ紐付け登録: <?= $view_name; ?></h1>
</div>
<div style="margin-top:1rem;">
	<div class="msg2">
		<?php if (!empty($csv_message['error_messages'])) : ?>
			<?php foreach ($csv_message['error_messages'] as $key => $data) : ?>
				<p>※<?= $data ?></p>
			<?php endforeach; ?>
		<?php endif; ?>
		<?php if (!empty($csv_message['message'])) : ?>
			<p><?= $csv_message['message'] ?></p>
		<?php endif; ?>
	</div>
	<p>以下の手順に従ってCSVを取り込んでください。</p>
	<p>1.記入用CSVをダウンロードしてください。</p>
	<div class="btnbox">
		<button onclick="csvDownload();return false;">記入用CSVダウンロード</button>
	</div>
	<p>2.ダウンロードしたCSVファイルに順位集計グループを入力してください。
		<br><span class="msg2">※CSVファイルの先頭行は削除しないでください。</span>
		<br><span class="msg2">※既に順位集計グループが登録済みの場合は上書きされます。</span>
		<br><span> ※ ファイル形式は「CSV UTF-8(カンマ区切り)」で保存してください。</span>
	</p>
	<p style="margin-bottom:0px;">3.作成したファイルを選択してください。<br><br>
	<div class="btnbox">
		<button class="import_file" type="button" onclick="openSelect();">ファイル選択</button>
		<div id="file_name"></div>
	</div>
	<p>4.登録するボタンを押して登録してください。</p>
	<div class="btnbox">
		<a href="/admin/grade/grade_setting_system/rank_aggregate_group/student/hr_list/<?= $grade_calc_group_id ?>" class="a_button">戻る</a>
		<button type="button" onclick="csvUpload();">登録する</button>
	</div>
</div>

<script type="text/javascript">
	function csvDownload() {
		$("#mode").val("csv_download");
		openDownloadModal('grade', 'csv_export');
	}

	function csvUpload() {
		var file_data = document.getElementById('csv_file').files[0];
		if (!file_data) {
			$('#file_name').html('<span class="msg2">※ファイルを選択してください</span>');
			return;
		}
		document.csv_import.submit();
	}

	function changeType() {
		$("#mode").val('change_type');
		document.csv_form.submit();
	}

	function registOption() {
		document.group_option.submit();
	}

	function openSelect() {
		$('#csv_file').click();
	}

	$(function() {
		$('#csv_file').on('change', function() {
			var file_data = document.getElementById('csv_file').files[0];
			$("#file_name").empty().html(file_data.name);
		});
	});
</script>

<?php $this->load->view('common/download_modal'); ?>

Controller

<?php
require_once "application/controllers/grade/AdminGradeController.php";
require_once dirname(__FILE__) . "/../../../libraries/ErrorBag.php";

use domain\model\UniqueId;

class AdminGradeSettingSystemRankCsvController extends AdminGradeController
{

	public function __construct()
	{
		parent::__construct();
		$this->load->helper("autoloader");
		blend_autoload();

		$this->load->model('GroupMember_m');
		$this->load->model('Homeroom_m');
		$this->load->model('GradeCalcGroups_m', '', TRUE);
		$this->load->model('GradeCalcGroupItems_m', '', TRUE);
		$this->load->model('GradeCalcGroupMembers_m', '', TRUE);

		$this->error_bag = new ErrorBag();

		if (empty($this->school_setting['use_blend2023'])) {
			show_error("不正なアクセスです", 404, "エラー");
		}

		$this->load->helper('Common');
		$this->load->library('CsvFile');
		$this->left_navi = 'grade_rank';
	}

	//順位集計グループ紐付け画面
	public function register($hr_grade, $grade_calc_group_id)
	{
		//CSVインポート時に発生したメッセージの設定
		$csv_message = array();
		if (!empty($this->session->userdata('error_messages'))) {
			$csv_message['error_messages'] = $this->session->userdata('error_messages');
		}
		if (!empty($this->session->userdata('message'))) {
			$csv_message['message'] = $this->session->userdata('message');
		}

		//画面名を設定
		$view_name = $hr_grade . "学年";
		$data = array(
			'body_id'     => 'admin',
			'body_class'  => 'admin grade grd_info',
			'header_menu' => 'grade',
			'page_title'  => '成績管理',
			'csv_message' => $csv_message,
			'grade_calc_group_id' => $grade_calc_group_id,
			'view_name'   => $view_name,
			'hr_grade'    => $hr_grade,
		);
		$data += $this->login_data;
		$this->addBreadList('HR一覧', '/admin/grade/grade_setting_system/rank_aggregate_group/student/hr_list/' . $grade_calc_group_id);
		$this->addBreadList('CSV登録', '/admin/grade/grade_setting_system/rank_aggregate_group/student/register/' . $hr_grade);
		$this->parser->parse('header', $data);
		$this->parser->parse('grade/grade_setting_system/rank_aggregate_group/csv/register', $data);
		$this->parser->parse('footer', $data);
	}

	//csvエクスポート
	public function export($hr_grade, $grade_calc_group_id)
	{

		//学年のHR情報を取得
		$school_id = $this->login_data['school_id'];
		$year = $this->login_data['year'];
		$homerooms = $this->Homeroom_m->getHomeRoomList($school_id, $year, $hr_grade);

		$grade_calc_groups = $this->GradeCalcGroups_m->getGradeCalcGroupsBySchoolId($school_id, $year);
		$grade_calc_group_items = $this->GradeCalcGroupItems_m->getGradeCalcGroupItemsBySchoolId($school_id, $year);

		foreach ($grade_calc_groups as $gcg_key => $grade_calc_group) {
			foreach ($grade_calc_group_items as $grade_calc_group_item) {
				if ($gcg_key === $grade_calc_group_item['grade_calc_group_id']) {
					$grade_calc_groups[$gcg_key]['grade_calc_group_item'][] = $grade_calc_group_item;
				}
			}
		}

		$hr_grade_list = array();
		$homeroom_list = array();
		foreach ($homerooms as $key => $homeroom) {
			$hr_grade = $homeroom['grade'];
			$hr_grade_list[] = $homeroom['grade'];
			$homeroom_list[$hr_grade][] = $homeroom;
		}
		$hr_grade_uniqe_list = array_unique($hr_grade_list);

		$student_list = array();
		foreach ($hr_grade_uniqe_list as $hr_grade_uniqeu) {
			foreach ($homeroom_list[$hr_grade_uniqeu] as $homeroom_list_key => $homeroom) {
				$students = array_column($this->GroupMember_m->getGroupMember($homeroom['id'], 1, false, false, array('is_teacher' => 1)), null, 'id');
				if (empty($students)) {
					continue;
				}
				$student_ids = array_column($students, 'id');
				$grade_calc_group_lists = array();
				foreach ($grade_calc_groups as $grade_calc_group_id_key => $grade_calc_group) {
					$grade_calc_group_lists[$grade_calc_group_id_key] = $this->GradeCalcGroupItems_m->getGradeCalcGroupItemsByAGroupIdAndStudentIds($grade_calc_group_id_key, $school_id, $year, $student_ids);
				}

				foreach ($students as $student_id_key => $student) {
					foreach ($grade_calc_group_lists as $grade_calc_group_key => $grade_calc_group_list) {
						$student_list[$student_id_key]['student_id'] = $student_id_key;
						$student_list[$student_id_key]['homeroom_grade'] = $student['homeroom_grade'];
						$student_list[$student_id_key]['homeroom_class_id'] = $student['homeroom_class_id'];
						$student_list[$student_id_key]['surname'] = $student['surname'];
						$student_list[$student_id_key]['name'] = $student['name'];
						$student_list[$student_id_key]['student_number'] = $student['student_number'];
						$student_list[$student_id_key]['student_name'] = $student['surname'] . ' ' . $student['name'];
						$student_list[$student_id_key]['class_name'] = $homeroom['class_name'];
						$student_list[$student_id_key]['leave_flg'] = $student['leave_flg'];
						if (!empty($grade_calc_group_list[$student_id_key])) {
							$student_list[$student_id_key]['grade_calc_group_item'][$grade_calc_group_key] = $grade_calc_group_list[$student_id_key];
						} else {
							$student_list[$student_id_key]['grade_calc_group_item'][$grade_calc_group_key] = array();
						}
					}
				}
			}
		}

		//CSVヘッダ
		$header = array();
		$header[] = 'Blend ID';
		$header[] = '学年';
		$header[] = '組';
		$header[] = '出席番号';
		$header[] = '名前(姓)';
		$header[] = '名前(名)';
		foreach ($grade_calc_groups as $key => $grade_calc_group) {
			$grade_calc_group_item_names = array_column($grade_calc_group['grade_calc_group_item'], 'grade_calc_group_item_name');
			foreach ($grade_calc_group_item_names as $key => $grade_calc_group_item_name) {
				$grade_calc_group_item_names[$key] = intval($key) + 1 . '.' . $grade_calc_group_item_name;
			}
			$header[] = $grade_calc_group['name'] . '(' . implode(' ', $grade_calc_group_item_names) . ')';
		}
		$csv_array[] = $header;

		//学年のHRから順位集計グループ情報を取得
		foreach ($student_list as $student_id_key => $student) {
			//CSVに出力する生徒ID、学年、組、出席番号、名前(姓)、名前(名)を配列に設定
			$data = array();
			$data[] = $student_id_key;
			$data[] = $student['homeroom_grade'];
			$data[] = $student['homeroom_class_id'];
			$data[] = $student['student_number'];
			$data[] = $student['surname'];
			$data[] = $student['name'];
			foreach ($student['grade_calc_group_item'] as $grade_calc_group_id_key => $grade_calc_group_item) {
				if (!empty($grade_calc_group_item)) {
					$data[] = $grade_calc_group_item['grade_calc_group_item_name'];
				} else {
					$data[] = '';
				}
			}
			$csv_array[] = $data;
		}

		//CSVファイル名を設定
		$filename = '順位集計グループ一括入力_' . $this->login_data['year'] . '年度' . $hr_grade . '学年.csv';
		$this->csvfile->download($filename, $csv_array);
		return;
	}

	//csvインポート
	public function import($hr_grade, $grade_calc_group_id)
	{
		$csv_path = $_FILES['csv_file']['tmp_name'];
		$csv_file = $this->csvfile->getDataArrayFromCSV($csv_path, false);

		if (!empty($csv_file['errors'])) {
			$return_result = $csv_file;
			return $return_result;
		}
		if ($csv_file[1][1] !== $hr_grade) {
			$flash = array(
				'message' =>  "※異なる学年のファイルは登録できません。正しい学年のファイルで登録を行なってください。",
				'status' => 'info-alert',
			);
			$this->session->set_flashdata($flash);
			redirect('/admin/grade/grade_setting_system/rank_aggregate_group/student/csv/register/' . $hr_grade . '/' . $grade_calc_group_id);
		}

		//学年のHR情報を取得
		$school_id = $this->login_data['school_id'];
		$year = $this->login_data['year'];
		$homerooms = $this->Homeroom_m->getHomeRoomList($school_id, $year, $hr_grade);
		if (empty($homerooms) || $homerooms[0]['school_id'] != $school_id) {
			show_error('HRが存在しません', 404, 'エラー');
		}
		$teacher_id = $this->login_data['teacher_id'];

		$grade_calc_groups = $this->GradeCalcGroups_m->getGradeCalcGroupsBySchoolId($school_id, $year);
		$grade_calc_group_items = $this->GradeCalcGroupItems_m->getGradeCalcGroupItemsBySchoolId($school_id, $year);

		$csv_file_data = array_column($csv_file, null, 0);
		$create_csv_data = array();
		foreach ($csv_file_data as $student_id_key => $data) {
			if ($student_id_key !== 'Blend ID') {
				$keys = array_keys($grade_calc_groups);
				$values = array_values(array_slice($data, 6));
				$new_array = array_combine($keys, $values);
				$create_csv_data[$student_id_key] = $new_array;
			}
		}

		foreach ($grade_calc_groups as $gcg_key => $grade_calc_group) {
			foreach ($grade_calc_group_items as $grade_calc_group_item) {
				if ($gcg_key === $grade_calc_group_item['grade_calc_group_id']) {
					$grade_calc_groups[$gcg_key]['grade_calc_group_item'][] = $grade_calc_group_item;
				}
			}
		}

		$hr_grade_list = array();
		$homeroom_list = array();
		foreach ($homerooms as $key => $homeroom) {
			$hr_grade = $homeroom['grade'];
			$hr_grade_list[] = $homeroom['grade'];
			$homeroom_list[$hr_grade][] = $homeroom;
		}
		$hr_grade_uniqe_list = array_unique($hr_grade_list);

		$student_list = array();
		$student_ids = array();
		foreach ($hr_grade_uniqe_list as $hr_grade_uniqeu) {
			foreach ($homeroom_list[$hr_grade_uniqeu] as $homeroom_list_key => $homeroom) {
				$students = array_column($this->GroupMember_m->getGroupMember($homeroom['id'], 1, false, false, array('is_teacher' => 1)), null, 'id');
				if (empty($students)) {
					continue;
				}
				$student_ids = array_column($students, 'id');
				$grade_calc_group_lists = array();
				foreach ($grade_calc_groups as $grade_calc_group_id_key => $grade_calc_group) {
					$grade_calc_group_lists[$grade_calc_group_id_key] = $this->GradeCalcGroupItems_m->getGradeCalcGroupItemsByAGroupIdAndStudentIds($grade_calc_group_id_key, $school_id, $year, $student_ids);
				}

				foreach ($students as $student_id_key => $student) {
					foreach ($grade_calc_group_lists as $grade_calc_group_key => $grade_calc_group_list) {
						$student_list[$student_id_key]['student_id'] = $student_id_key;
						$student_list[$student_id_key]['homeroom_grade'] = $student['homeroom_grade'];
						$student_list[$student_id_key]['homeroom_class_id'] = $student['homeroom_class_id'];
						$student_list[$student_id_key]['surname'] = $student['surname'];
						$student_list[$student_id_key]['name'] = $student['name'];
						$student_list[$student_id_key]['student_number'] = $student['student_number'];
						if (empty($create_csv_data[$student_id_key]) || $create_csv_data[$student_id_key][$grade_calc_group_key] === '') {
							$student_list[$student_id_key]['grade_calc_group_item'][$grade_calc_group_key] = array();
						} else {
							if(!in_array($create_csv_data[$student_id_key][$grade_calc_group_key], array_column($grade_calc_groups[$grade_calc_group_key]['grade_calc_group_item'], 'grade_calc_group_item_name')) ) {
								if(!is_numeric($create_csv_data[$student_id_key][$grade_calc_group_key])) {
									$flash = array(
										'message' =>  "※項目「{$grade_calc_groups[$grade_calc_group_key]['name']}」に設定できない値が入力されています。",
										'status' => 'info-alert',
									);
									$this->session->set_flashdata($flash);
									redirect('/admin/grade/grade_setting_system/rank_aggregate_group/student/csv/register/' . $hr_grade . '/' . $grade_calc_group_id);
								}
							}
							foreach ($grade_calc_groups[$grade_calc_group_key]['grade_calc_group_item'] as $grade_calc_group_item) {
								if ($grade_calc_group_item['grade_calc_group_item_name'] === $create_csv_data[$student_id_key][$grade_calc_group_key]) {
									$student_list[$student_id_key]['grade_calc_group_item'][$grade_calc_group_key] = array(
										'grade_calc_group_id' => $grade_calc_group_key,
										'id' => $grade_calc_group_item['grade_calc_group_item_id'],
										'student_id' => $student_id_key,
										'grade_calc_group_item_name' => $create_csv_data[$student_id_key][$grade_calc_group_key],
									);
									continue;
								} elseif (!in_array($create_csv_data[$student_id_key][$grade_calc_group_key], array_column($grade_calc_groups[$grade_calc_group_key]['grade_calc_group_item'], 'grade_calc_group_item_name')) && is_numeric($create_csv_data[$student_id_key][$grade_calc_group_key])) {
									foreach ($grade_calc_groups[$grade_calc_group_key]['grade_calc_group_item'] as $key => $item) {
										$grade_calc_group_item_number = $key + 1;
										if ($grade_calc_group_item_number === intval($create_csv_data[$student_id_key][$grade_calc_group_key])) {
											$student_list[$student_id_key]['grade_calc_group_item'][$grade_calc_group_key] = array(
												'grade_calc_group_id' => $grade_calc_group_key,
												'id' => $item['grade_calc_group_item_id'],
												'student_id' => $student_id_key,
												'grade_calc_group_item_name' => $create_csv_data[$student_id_key][$grade_calc_group_key],
											);
											continue;
										}
									}
									if (count($grade_calc_groups[$grade_calc_group_key]['grade_calc_group_item']) < intval($create_csv_data[$student_id_key][$grade_calc_group_key])) {
										$flash = array(
											'message' =>  "※項目「{$grade_calc_groups[$grade_calc_group_key]['name']}」に設定できない数字が入力されています。",
											'status' => 'info-alert',
										);
										$this->session->set_flashdata($flash);
										redirect('/admin/grade/grade_setting_system/rank_aggregate_group/student/csv/register/' . $hr_grade . '/' . $grade_calc_group_id);
									}
								}
							}
						}
					}
				}
			}
		}

		//登録処理実施後に表示するメッセージの初期化
		$error_messages = array();

		//固定値バリデーションチェック
		$error_messages = $this->csvValidation($csv_file, $student_list);
		if (!empty($error_messages)) {
			$this->session->set_flashdata('error_messages', $error_messages);
			redirect('/admin/grade/grade_setting_system/rank_aggregate_group/student/csv/register/' . $hr_grade . '/' . $grade_calc_group_id);
		}

		//トランザクション登録処理開始
		$this->db->trans_begin();

		$is_delete_list = array();
		foreach (array_keys($student_list) as $student_id) {
			$this->db->where('student_id', $student_id);
			$this->db->where('school_id', $school_id);
			$is_delete_list[] = $this->db->delete('grade_calc_group_members');
		}

		$is_insert_list = array();
		foreach ($student_list as $student_id_key => $student) {
			foreach ($student['grade_calc_group_item'] as $grade_calc_group_id_key => $grade_calc_group_item) {
				if (!empty($grade_calc_group_item)) {
					$gcgi_uuid = new UniqueId();
					$create_data = array(
						'id' => $gcgi_uuid->value(),
						'school_id' => $school_id,
						'grade_calc_group_id' => $grade_calc_group_id_key,
						'grade_calc_group_item_id' => $grade_calc_group_item['id'],
						'student_id' => $student_id_key,
						'year' => $year,
						'created_at' => date('Y-m-d H:i:s'),
						'created' => $teacher_id,
					);
					$is_insert_list[] = $this->db->insert('grade_calc_group_members', $create_data, true);
				}
			}
		}

		if ($this->db->trans_status() === false || in_array(false, $is_delete_list) || in_array(false, $is_insert_list)) {
			$this->db->trans_complete();
			$this->db->trans_rollback();
			$flash = array(
				'message' => '※データベースへの登録に失敗しました。',
				'status' => 'info-alert',
			);
			$this->session->set_flashdata($flash);
			redirect('/admin/grade/grade_setting_system/rank_aggregate_group/student/csv/register/' . $hr_grade . '/' . $grade_calc_group_id);
		} else {

			$this->db->trans_commit();
			$flash = array(
				'message' => '※' . $hr_grade . '年生の集計グループ紐付けを更新しました',
				'status' => 'info-alert',
			);
			$this->session->set_flashdata($flash);
			redirect('/admin/grade/grade_setting_system/rank_aggregate_group/student/hr_list/' . $grade_calc_group_id);
		}
	}


	private function csvValidation($csv_file, $student_list)
	{
		$error_message = [];
		//CSVヘッダをチェック
		if (!isset($csv_file[0])) {
			$error_message[] = '不正なフォーマットです';
		}

		foreach ($csv_file as $row_num => $csv_data) {
			if ($row_num == 0) continue;

			// 順位集計グループ項目以外の項目をチェック
			$student_id = $csv_data[0];
			$homeroom_grade = $csv_data[1];
			$homeroom_class_id = $csv_data[2];
			$student_number = $csv_data[3];
			$surname = $csv_data[4];
			$name = $csv_data[5];

			if (empty($student_list[$student_id])) {
				$error_message[] = "{$row_num}行目の生徒は存在しません";
			} else {
				$checking_student = $student_list[$student_id];
				if ($checking_student['homeroom_grade'] != $homeroom_grade) {
					$error_message[] = "{$row_num}行目の生徒の学年が異なります";
				}
				if ($checking_student['homeroom_class_id'] != $homeroom_class_id) {
					$error_message[] = "{$row_num}行目の生徒の組が異なります";
				}
				if ($checking_student['student_number'] != $student_number) {
					$error_message[] = "{$row_num}行目の生徒の出席番号が異なります";
				}
				if ($checking_student['surname'] != $surname) {
					$error_message[] = "{$row_num}行目の生徒の姓が異なります";
				}
				if ($checking_student['name'] != $name) {
					$error_message[] = "{$row_num}行目の生徒の名が異なります";
				}
			}
		}
		return $error_message;
	}
}

新規作成

既存バリデーション機能

フロント

<style>
	section.flex {
		justify-content: space-between;
	}

	.info-alert {
		padding: 0.5rem 0;
		color: #FF0000;
	}

	.cmnListTable table tbody tr:hover th,
	.cmnListTable table tbody tr:hover td {
		background: transparent;
	}

	.cmnListTable__ {
		padding: 1rem;
	}

	.rank-aggregate-group-create-table {
		width: 100%;
		margin-top: 30px;
	}

	.rank-aggregate-group-create-table tbody tr th {
		width: 30%;
	}

	.rank-aggregate-group-create-table tbody tr td {
		width: 60%;
		text-align: left;
	}

	.rank-aggregate-group-create-table tbody tr td div p {
		padding: 0.5rem 0;
	}

	.rank-aggregate-group-create-table tbody tr td div p input {
		width: 76.5%;
	}

	.fa-minus-circle {
		cursor: pointer;
		font-size: 20px;
	}

	.sort-btn-box {
		display: flex;
		width: 100%;
	}

	.sort-btn {
		display: block;
		background-color: #ddd;
		color: #fff;
		width: 50%;
		height: 20px;
		line-height: 20px;
		font-size: 15px;
		margin: 5px;
	}

	.sort-btn:hover {
		background-color: #333;
	}

	.sort-btn:active {
		background-color: #eee;
	}

	.sort-btn.inactive {
		background-color: #fafafa;
		pointer-events: none;
	}

	.btnbox {
		width: 100%;
	}

	.btnbox.btn-wrap {
		max-width: initial;
		text-align: center;
		margin: 0 auto;
	}

	.btnbox.btn-wrap button {
		width: 140px;
	}

	.btnbox.btn-wrap button:last-child {
		width: 220px;
	}

	.btnbox .back.white {
		background-color: #CCC;
		color: #000;
		cursor: pointer;
	}

	.btnbox button {
		margin: 0 0.5rem;
	}

	.cmnInput {
		background: #FFF;
		border: 1px solid #BBB;
		border-radius: 4px;
		height: 50px;
		width: 80%;
		text-align: center;
		font-size: 15px;
	}
</style>

<section class="flex">
	<div>
		<form action="/admin/grade/grade_setting_system/rank_aggregate_group/create" method="post" name="create" id="postRankAggregateGroup">
			<input type="hidden" name="create" id="inputRankAggregateGroup" value="">
			<input type="hidden" name="is_hr_list" id="inputIsHrList" value="">
		</form>
	</div>

	<div class="cmnListTable cmnListTable--overflow" style="margin-top:30px;">
		<div class="cmnListTable__">
			<p>集計グループ 新規作成</p>
			<?php if (!empty($message['valid_message'])) : ?>
				<div class="<?= $message['status'] ?>">
					<?php foreach ($message['valid_message'] as $valid_message) : ?>
						<p><?= !empty($valid_message['group_name']) ? '※' . $valid_message['group_name'] : '' ?></p>
						<p><?= !empty($valid_message['unique_name']) ? '※' . $valid_message['unique_name'] : ''; ?></p>
						<p><?= !empty($valid_message['name']) ? '※' . $valid_message['name'] : '' ?></p>
						<p><?= !empty($valid_message['same_name']) ? '※' . $valid_message['same_name'] : '' ?></p>
					<?php endforeach; ?>
				</div>
			<?php endif; ?>
			<?php if (!empty($message['message'])) : ?>
				<div class="<?= $message['status'] ?>">
					<p><?= $message['message'] ?></p>
				</div>
			<?php endif; ?>
			<table class="rank-aggregate-group-create-table">
				<tbody>
					<tr>
						<th>グループ種別</th>
						<td>
							<?php if (empty($post_group_name)) : ?>
								<input id="aggregateGroupType" class="cmnInput" type="text" value="">
							<?php else : ?>
								<input id="aggregateGroupType" class="cmnInput" type="text" value="<?= $post_group_name ?>">
							<?php endif; ?>
						</td>
					</tr>
					<tr>
						<th>グループ項目</th>
						<td>
							<?php if (empty($post_items_data)) : ?>
								<div class="input">
									<p><span class="input_number">1</span>. <input class="cmnInput" type="text" value="">
										<span style="margin-left: 10px; display: inline-block; visibility: hidden;"><i class="fa fa-minus-circle"></i>
										</span>
									</p>
								</div>
							<?php else : ?>
								<div class="input">
									<?php foreach ($post_items_data as $key => $post_item) : ?>
										<?php if ($key === 1) : ?>
											<p><span class="input_number"> <?= $key ?></span>. <input class="cmnInput" type="text" value="<?= $post_item ?>">
												<span style="margin-left: 10px; display: inline-block; visibility: hidden;">
													<i data-id="<?= $key ?>" class="fa fa-minus-circle minus-button"></i>
												</span>
											<?php else : ?>
											<p id="deleteInput<?= $key ?>"><span class="input_number"> <?= $key ?></span>. <input class="cmnInput" type="text" value="<?= $post_item ?>">
												<span style="margin-left: 10px; display: inline-block;">
													<i data-id="<?= $key ?>" class="fa fa-minus-circle minus-button"></i>
												</span>
											</p>
										<?php endif; ?>
									<?php endforeach; ?>
								</div>
							<?php endif; ?>
							<div>
								<button id="addFormRankAggregateGroup" class='plus-button' type='button'>
									<i class='fa fa-plus-circle'></i>選択肢を追加する
								</button>
							</div>
						</td>
					</tr>
				</tbody>
			</table>
		</div>
		<div class="btnbox btn-wrap">
			<button type="button" class="back white" onclick="location.href='/admin/grade/grade_setting_system/rank_aggregate_group/';">戻る</button>
			<button type="button" class="submit createAggregateGroup" data-is-hr-list="0">作成する</button>
			<button type="button" class="submit createAggregateGroup" data-is-hr-list="1">作成して生徒を紐付ける</button>
		</div>
	</div>
</section>
<script type="text/javascript">
	$(function() {
		$('#addFormRankAggregateGroup').on('click', function() {
			let input_parent = $('.rank-aggregate-group-create-table tbody tr td div.input');
			let input_length = $('.rank-aggregate-group-create-table tbody tr td div.input p').length;
			$(input_parent).append('<p id="deleteInput' + Number(input_length + 1) + '"><span class="input_number">' + Number(input_length + 1) + '</span>. <input class="cmnInput" type="text"><span style="margin-left: 10px; display: inline-block;"><i data-id="' + Number(input_length + 1) + '" class="fa fa-minus-circle minus-button"></i></span></p>');
		});

		$(document).on('click', '.minus-button', function() {
			let id = $(this).data('id');
			$('#deleteInput' + id).remove();
			let count = 1;
			$('.input_number').each(function(index, data) {
				if (index !== 0) {
					$(data).text(count);
					$(data).parent().attr('id', 'deleteInput' + count);
					$(data).parent().find('.minus-button').attr('data-id', count);
				}
				count++;
			});
		});

		$('.createAggregateGroup').on('click', function() {
			const rankAggregateGroupMap = new Map();
			$('.cmnInput').each((index, data) => {
				rankAggregateGroupMap.set(index, $(data).val());
			});
			const json_data = JSON.stringify(Object.fromEntries(rankAggregateGroupMap));
			$('#inputRankAggregateGroup').val(json_data);
			$('#inputIsHrList').val($(this).data('is-hr-list'));
			$('#postRankAggregateGroup').submit();
		});
	})
</script>

Controller

	public function gradeCalcGroupResister()
	{
		if (empty($this->school_setting['use_blend2023'])) {
			redirect('/admin/grade/grade_setting_system/rank');
		}
		$message = array();
		$post_group_name = array();
		$post_items_data = array();
		if (!empty($this->session->userdata('valid_message'))) {
			$message['valid_message'] = $this->session->userdata('valid_message');
			$post_group_name = $this->session->userdata('group_name');
			$post_items_data = $this->session->userdata('post_items_data');
			unset($post_items_data[0]);
			$message['status'] = $this->session->userdata('status');
		}
		if (!empty($this->session->userdata('message'))) {
			$message['message'] = $this->session->userdata('message');
			$post_group_name = $this->session->userdata('group_name');
			$post_items_data = $this->session->userdata('post_items_data');
			unset($post_items_data[0]);
			$message['status'] = $this->session->userdata('status');
		}

		$data = array(
			'body_id'                  => 'admin',
			'body_class'               => 'admin grade grd_info',
			'header_menu'              => 'grade',
			'page_title'               => '順位集計グループ 新規作成',
			'is_admin_user'            => $this->is_admin_user,
			'message'                  => $message,
			'post_group_name'          => $post_group_name,
			'post_items_data'          => $post_items_data,
		);

		$data += $this->login_data;

		//パンくずリスト
		$this->addBreadList('順位集計グループ', '/admin/grade/grade_setting_system/rank_aggregate_group/');
		$this->addBreadList('新規作成');
		$this->parser->parse("header", $data);
		$this->parser->parse("grade/grade_setting_system/rank_aggregate_group/resister", html_escape($data));
		$this->parser->parse("footer", $data);
	}

	public function gradeCalcGroupCreate()
	{
		$school_id = $this->login_data['school_id'];
		$teacher_id = $this->login_data['teacher_id'];
		$year = $this->login_data['year'];
		$post_data = json_decode($this->input->post('create'), true);
		$is_hr_list = $this->input->post('is_hr_list');

		// バリデーションチェック
		$this->load->library('form_validation');
		$errors = array();
		$post_data_list = array();
		foreach ($post_data as $key => $data) {
			if (intval($key) !== 0) {
				$compare_items_data = $post_data;
				unset($compare_items_data[0]);
				unset($compare_items_data[$key]);
				$data === '' ? null : $data;
				if (in_array($data, $compare_items_data, true) && empty($errors)) {
					$errors[] = array('same_name' => '同じ名前のグループ項目が入力されています。');
				}
				$post_data_list['name'] = $data;
				$this->form_validation->set_data($post_data_list);
				$this->form_validation->set_rules('name', 'グループ項目', 'required|trim|max_length[20]');
				$this->form_validation->run();
			} else {
				$search_group_name = $this->GradeCalcGroups_m->getGradeCalcGroupByName($school_id, $year, $data);
				if (!empty($search_group_name)) {
					$errors[] = array('unique_name' => 'すでに同じ名前のグループ種別が登録されています。');
				};
				$post_data_list['group_name'] = $data;
				$this->form_validation->set_data($post_data_list);
				$this->form_validation->set_rules('group_name', 'グループ種別', 'required|trim|max_length[20]');
				$this->form_validation->run();
			}
		}

		$errors[] = $this->form_validation->error_array();
		if (!empty($errors[0])) {
			$flash = array(
				'group_name'      => $post_data[0],
				'post_items_data' => $post_data,
				'valid_message'   => $errors,
				'status'          => 'info-alert',
			);
			$this->session->set_flashdata($flash);
			redirect('/admin/grade/grade_setting_system/rank_aggregate_group/resister');
		}

		//***** トランザクション開始 *****
		$this->db->trans_start();
		$gcg_uuid = new UniqueId();
		$create_data = array(
			'id' => $gcg_uuid->value(),
			'school_id' => $school_id,
			'name' => $post_data[0],
			'year' => $year,
			'created_at' => date('Y-m-d H:i:s'),
			'created' => $teacher_id,
		);
		$is_gcg_insert = $this->db->insert('grade_calc_groups', $create_data, true);

		$is_gcgi_insert_list = array();
		foreach ($post_data as $key => $data) {
			$gcgi_uuid = new UniqueId();
			if (intVal($key) !== 0) {
				$create_data = array(
					'id' => $gcgi_uuid->value(),
					'school_id' => $school_id,
					'grade_calc_group_id' => $gcg_uuid->value(),
					'name' => $data,
					'year' => $year,
					'created_at' => date('Y-m-d H:i:s'),
					'created' => $teacher_id,
				);
				$is_gcgi_insert_list = $this->db->insert('grade_calc_group_items', $create_data, true);
			}
		}

		if ($this->db->trans_status() !== false && $is_gcg_insert && !in_array(false, $is_gcgi_insert_list)) {
			$this->db->trans_complete();
			$this->db->trans_commit();
		} else {
			$this->db->trans_rollback();
			$flash = array(
				'group_name'      => $post_data[0],
				'post_items_data' => $post_data,
				'message'         => 'データベースへの登録に失敗しました。',
				'status'          => 'info-alert',
			);
			$this->session->set_flashdata($flash);
			redirect('/admin/grade/grade_setting_system/rank_aggregate_group/resister');
		}
		//***** トランザクション終了 *****
		if (intVal($is_hr_list)) {
			redirect('/admin/grade/grade_setting_system/rank_aggregate_group/student/hr_list/' . $gcg_uuid->value());
		} else {
			$flash = array(
				'message'         => '順位集計グループを追加しました。',
				'status'          => 'info-alert',
			);
			$this->session->set_flashdata($flash);
			redirect('/admin/grade/grade_setting_system/rank_aggregate_group');
		}
	}

科目表示制御

フロント

	<div class="subjects-header-wrap">
		<h2><?= $curriculum_guideline_name ?> <?= $report_type_name ?>設定</h2>
		<div class="header-items-wrap">
			<p style="display: inline-block;">各帳票に表示する、教科名・科目名の名称・並び順・非表示設定を行います。</p>
			<p style="display: inline-block;">帳票に表示する表示名を編集してください。</p>
			<p style="display: inline-block;">表示名が未入力の場合、デフォルトの教科・科目名が表示されます。</p>
			<p style="display: inline-block;">帳票に表示しない場合は、「非表示」の列にチェックを入れてください。</p>
			<p style="display: inline-block;">表示順を変更する場合は各科目をドラッグアンドドロップで並び替えてください。</p>
			<p class="flash-massage <?= $this->session->flashdata('status'); ?>"><?= $this->session->flashdata('message'); ?></p>
		</div>
	</div>
	<section>
		<form id="updateSchoolReportSubjectReplaceForm" action="/admin/school/subject_setting/update" method="post">
			<div class="btnbox right">
				<button type="button" class="back white" style="width:140px;" onclick="location.href='/admin/school/subject_setting/detail/';">戻る</button>
				<button type="button" class="submit" style="width:140px;" id="updateSchoolReportSubjectReplace">更新する</button>
			</div>
			<div class="subject-list-wrap">
				<table id="editSubjectList" class="sortable">
					<thead class="table-length" style="background-color: #DADADA;">
						<tr class="tr-length" style="background-color: #DADADA;">
							<th class="subject-list-item" style="background-color: #DADADA; width: 9.4rem;"></th>
							<th class="subject-list-item" style="background-color: #DADADA;">教科</th>
							<th class="subject-list-item" style="background-color: #DADADA;">科目</th>
							<th class="subject-list-item" style="background-color: #DADADA;">教科 表示名</th>
							<th class="subject-list-item" style="background-color: #DADADA;">科目 表示名</th>
							<th class="subject-list-item subject-list-item-wrap" style="background-color: #DADADA;">
								<div>非表示</div>
								<div><input type="checkbox" id="checkDisplayFlg"></div>
							</th>
						</tr>
					</thead>
					<tbody class="sortable-tbody table-length">
						<?php foreach ($school_report_subject_replace_array as $key => $school_report_subject_replace) : ?>
							<?php if (($report_type === $school_report_subject_replace['report_type']) || (is_null($school_report_subject_replace['report_type']))) : ?>
								<tr class="sortable-item tr-length" data-subject-id="<?= $school_report_subject_replace['subject_id'] ?>" data-sub-subject-id="<?= $school_report_subject_replace['sub_subject_id'] ?>" data-position="">
									<th class="subject-list-item move-icon sortable-item-handle icon" style="width: 9.4rem;" rowspan=""><i class="fa fa-th" aria-hidden="true"></i></td>
									<td class="sortable-item-group-handler sortable-item-handle" data-subject-id="<?= $school_report_subject_replace['subject_id'] ?>"><?= $school_report_subject_replace['subject_name'] ?></td>
									<td class="sortable-item-handler sortable-item-handle">
										<div class="sortable-item-handler-items">
											<div><?= $school_report_subject_replace['sub_subject_name'] ?></div>
											<div><i class="fa fa-th" aria-hidden="true"></i></div>
										</div>
									</td>
									<td class="td-form-style"><input class="s-name cmnInput" type="text" value="<?= $school_report_subject_replace['s_name'] ?>"></td>
									<td class="td-form-style"><input class="ss-name cmnInput" type="text" value="<?= $school_report_subject_replace['ss_name'] ?>"></td>
									<?php if (intVal($school_report_subject_replace['display_flg']) === 0) : ?>
										<td class="display-flg-style"><input class="display-flg" type="checkbox" value="<?= $school_report_subject_replace['display_flg'] ?>" checked></td>
									<?php else : ?>
										<td class="display-flg-style"><input class="display-flg" type="checkbox" value="<?= $school_report_subject_replace['display_flg'] ?>"></td>
									<?php endif; ?>
								</tr>
							<?php endif; ?>
						<?php endforeach; ?>
					</tbody>
				</table>
				<input id="formDataSchoolReportSubjectReplaces" name="school_report_subject_replaces" type="hidden" value="">
				<input name="report_type" type="hidden" value="<?= $report_type ?>">
				<input name="report_type_name" type="hidden" value="<?= $report_type_name ?>">
				<input name="curriculum_guideline_type" type="hidden" value="<?= $curriculum_guideline_id ?>">
			</div>
		</form>
	</section>
	<script>
		$(function() {
			// 検索したいsubject_id
			let target_subject_id = '';

			/** 同じ要素の最後のindexを取得
			 * @param {Array} array
			 * @param {Number} start_index
			 * @param {Boolean} callback
			 */
			function fnHitIndexSome(array, start_index, callback) {
				let hitIndex = null;
				let not_verification_array = [];
				not_verification_array = array.filter(function(value) {
					return $(value).data('subject-id') !== 'verification';
				});
				not_verification_array.some((data, index) => {
					if (callback(data)) {
						hitIndex = index;
						return false;
					} else {
						return true;
					}
				});

				return hitIndex;
			}

			/** テーブル左側にバーを教科別に表示
			 * @param {Number} $change_status //1.初期化 2.一覧の更新
			 */
			function fnAddIconLeftOfSubjects($change_status) {
				let first_hit_index = 0;
				let tr_verification_array = [];
				let tr_add_icon_array = [];
				$('.move-icon').attr('rowspan', '');
				$('.move-icon').hide();
				let sortable_items_clone = $('.sortable-item').clone();

				if ($change_status) {
					sortable_items_clone.remove();
					sortable_items_clone = $('.sortable-item').clone();
				}

				// tbody内のtrのdomをリストで取得
				$('.sortable-item').each((index, data) => {
					tr_add_icon_array[index] = data;
				});
				$.each(sortable_items_clone, (index, data) => {
					tr_verification_array[index] = data;
				});

				// 初回の検索
				first_data = tr_verification_array[0];
				first_subject_id = $(first_data).data('subject-id');
				let hitIndex = fnHitIndexSome(tr_verification_array, 0, (data) => {
					return ($(data).data('subject-id') === first_subject_id);
				});
				first_hit_index = hitIndex;

				for (let i = 0; i <= first_hit_index; i++) {
					$(tr_verification_array[i]).data('subject-id', 'verification');
				}
				$(tr_add_icon_array[0]).find('.move-icon').attr('rowspan', first_hit_index + 1);
				$(tr_add_icon_array[0]).find('.move-icon').show();

				// 2回目以降の検索
				let for_length = tr_verification_array.length;
				let target_index = '';
				let next_tr_target_index = '';
				let times = 0;
				for (let i = first_hit_index + 1; i <= for_length; i++) {
					let target_index = next_tr_target_index;
					if (target_index === '') {
						// 2回目検索時の開始index
						target_index = i;
						next_tr_target_index = i;
					} else {
						// 3回目以降検索時の開始index
						target_index = next_tr_target_index;
					}

					target_subject_id = $(tr_verification_array[target_index]).data('subject-id');
					let hitIndex = fnHitIndexSome(tr_verification_array, target_index, (data) => {
						return ($(data).data('subject-id') === target_subject_id);
					});
					// indexがヒットしなかったらループを抜ける。
					if (hitIndex === null) {
						break;
					}
					if (times === 0) {
						$(tr_add_icon_array[first_hit_index + 1]).find('.move-icon').attr('rowspan', hitIndex + 1);
						$(tr_add_icon_array[first_hit_index + 1]).find('.move-icon').show();
					} else {
						$(tr_add_icon_array[target_index]).find('.move-icon').attr('rowspan', hitIndex + 1);
						$(tr_add_icon_array[target_index]).find('.move-icon').show();
					}

					next_tr_target_index = next_tr_target_index + hitIndex + 1;
					for (let ix = 0; ix < next_tr_target_index; ix++) {
						$(tr_verification_array[ix]).data('subject-id', 'verification');
					}
					times = 1;
				}
			}

			// ----------  一覧の初期化  ----------
			fnAddIconLeftOfSubjects(0);

			// ---------- table内イベントの初期化  ----------
			fnAddEventAfterRemove();

			// ----------  選択の非活性化  ----------
			$(document).on('click', function(e) {
				fnAddEventAfterRemove();
				if (!$(e.target).closest('#editSubjectList').length) {
					$('.sortable-tbody').find('.selected').each(function(index, data) {
						$(data).removeClass('selected');
					});
				}
			});

			/**  選択した教科の前後にある同じ教科にも「.selected」を付与
			 * @param {Object} b_sortable_items //選択した要素より前に位置する同じ科目の要素
			 * @param {Object} a_sortable_items //選択した要素より後に位置する同じ科目の要素
			 * @param {Int} current_item //選択した要素より後に位置する同じ科目の要素
			 **/
			function fnSelectedItemsAddClass(b_sortable_items, a_sortable_items, current_item) {
				$.each(b_sortable_items, function(index, data) {
					if ($(data).data('subject-id') !== $(current_item).data('subject-id')) {
						return false;
					} else {
						$(data).addClass('selected');
					}
				});
				$.each(a_sortable_items, function(index, data) {
					if ($(data).data('subject-id') !== $(current_item).data('subject-id')) {
						return false;
					} else {
						$(data).addClass('selected');
					}
				});
			}

			// ----------  更新ボタンクリック時の処理  ----------
			$('#updateSchoolReportSubjectReplace').on('click', function() {
				const schoolReportSubjectReplaceMap = new Map();
				$('.sortable-item').each((index, data) => {
					let obj = new Object();
					obj.sub_subject_id = $(data).data('sub-subject-id');
					obj.subject_name = $(data).find('td.sortable-item-group-handler').text();
					obj.sub_subject_name = $(data).find('td .sortable-item-handler-items div:first').text();
					obj.s_name = $(data).find('td .s-name.cmnInput').val();
					obj.ss_name = $(data).find('td .ss-name.cmnInput').val();
					obj.sort_no = index + 1;
					obj.display_flg = $(data).find('td .display-flg').val();

					schoolReportSubjectReplaceMap.set($(data).data('sub-subject-id'), obj);
				});
				const json_data = JSON.stringify(Object.fromEntries(schoolReportSubjectReplaceMap));
				$('#formDataSchoolReportSubjectReplaces').val(json_data);
				$('form#updateSchoolReportSubjectReplaceForm').submit();
			});

			// ----------  「科目」「非表示」項目のwidthを取得 (sortableで使用) ----------
			let icon_width = $('.school.subjects tr>th.subject-list-item.move-icon').innerWidth();
			let subject_width = $('.sortable-item-group-handler').outerWidth();
			let sub_subject_width = $('.sortable-item-handler').outerWidth();
			let td_form_style = $('.td-form-style').outerWidth();
			let display_flg_width = $('.display-flg-style').outerWidth();

			// ----------  sortableのstartイベントが2連続発動時に使用する変数・オブジェクト  ----------
			let sortable_start_eve_times = 1;
			let helper_item_obj = {
				helper_item: '',
				selected_item_all: '',
				helper_item_width: '',
				item_height: '',
				getHelperItem() {
					return this.helper_item;
				},
				getSelectedItemAll() {
					return this.selected_item_all;
				},
				getHelperItemWidth() {
					return this.helper_item_width;
				},
				getHelperItemHeight() {
					return this.item_height;
				},
				setHelperItem(param) {
					this.helper_item = param;
				},
				setSelectedItemAll(param) {
					this.selected_item_all = param;
				},
				setHelperItemWidth(param) {
					this.helper_item_width = param;
				},
				setHelperItemHeight(param) {
					this.item_height = param;
				},
			};
			let item_obj = {
				item: '',
				getItem() {
					return this.item;
				},
				setItem(param) {
					this.item = param;
				},
			}

			// ----------  sortable機能(要素のドラッグ&ドロップ)  ----------
			$('.sortable tbody').sortable({
				axis: 'y',
				items: '> .sortable-item',
				handle: '.sortable-item-handle',
				scroll: true,
				helper: function(e, item) {
					if (sortable_start_eve_times === 2) {
						// start 2連続発火時
						$('.ui-sortable-helper').remove();
						item = item_obj.getItem();
						if (!item.hasClass('selected')) {
							// 対象の全ての要素にselectedをつける(itemをドラッグ中に削除するため)
							item.parent().children('.selected').removeClass('selected');
							item.addClass('selected');
						}
						$('.move-icon').attr('rowspan', '');
						$('.move-icon').show();
						ui_selected_all = helper_item_obj.getSelectedItemAll();
						item.data('multidrag', ui_selected_all).siblings('.selected').remove();
						ph = helper_item_obj.getHelperItemHeight();
						helper_item = helper_item_obj.getHelperItem();
						helper_item.width(helper_item_obj.getHelperItemWidth());

						return helper_item.append(ui_selected_all);

					} else {
						// 正常時
						if (!item.hasClass('selected')) {
							// 対象の全ての要素にselectedをつける(itemをドラッグ中に削除するため)
							item.parent().children('.selected').removeClass('selected');
							item.addClass('selected');
						}
						sortable_start_eve_times = 1;
						$('.move-icon').attr('rowspan', '');
						$('.move-icon').show();
						ui_selected_all = item.parent().children('.selected').clone();
						ui_selected_all.children('.move-icon').width(icon_width);
						ui_selected_all.children('.sortable-item-group-handler').width(subject_width);
						ui_selected_all.children('.sortable-item-handler').width(sub_subject_width);
						ui_selected_all.children('.td-form-style').width(td_form_style);
						ui_selected_all.children('.display-flg-style').width(display_flg_width);
						$(ui_selected_all).width($(item).width());
						helper_width = $(ui_selected_all).width();

						// itemを削除(削除した分の高さを保持してstart時に充てる)
						item_height = 1;
						if (ui_selected_all.length > 2) {
							item_height = 3;
						}
						ph = item.outerHeight() * item_height;
						item.data('multidrag', ui_selected_all).siblings('.selected').remove();

						helper_item = $('<tr>');
						helper_item.width(helper_width);

						// オブジェクトに格納
						item_obj.setItem(item);

						helper_item_obj.setHelperItem(helper_item);
						helper_item_obj.setSelectedItemAll(ui_selected_all);
						helper_item_obj.setHelperItemWidth(helper_width);
						helper_item_obj.setHelperItemHeight(ph);

						return helper_item.append(ui_selected_all);
					}
				},
				start: function(e, ui) {
					sortable_start_eve_times = 2;
					// 選択した要素分の高さを充てる
					ui.placeholder.css({
						'height': ph,
						"visibility": "visible",
						"background-color": "orange"
					});
					fnFixTop(ui);
					fnAddEventAfterRemove();
				},
				stop: function(e, ui) {
					sortable_start_eve_times = 1;
					let selected = ui.item.data('multidrag');
					// 移動させた一番上の要素下に選択した全ての要素を突っ込む
					ui.item.after(selected);
					// 一番上の要素を削除
					ui.item.remove();
					fnAddIconLeftOfSubjects(1);
				},
				update: function(event, ui) {
					$(this).children().each((index, data) => {
						$(data).attr('data-position', index + 1);
					});
					fnAddEventAfterRemove();
				},
				// スクロール時に要素の位置を調整
				sort: function(event, ui) {
					fnFixTop(ui);
				},
			});

			// テーブル上の座標を取得
			let client_y = ''
			$('.subject-list-wrap').on('mousemove', (e) => {
				client_y = e.clientY;
			});

			/**  ドラッグ時のtop位置の修正
			 * @param {Object} ui //sortableのオブジェクト
			 **/
			function fnFixTop(ui) {
				ui.helper.offset({
					top: client_y,
				});
			}

			/**  イベント登録  **/
			function fnAddEventAfterRemove() {
				// ----------  教科のマウスダウンイベント  ----------
				$('.sortable-item-group-handler, .subject-list-item.move-icon').on('mousedown', function() {
					$('.sortable-tbody').find('.selected').each(function(index, data) {
						$(data).removeClass('selected');
					});
					$(this).parent().addClass('selected');
					let target = $('.selected');

					let current_item = $(this).parent();
					let current_index = $('tbody tr.sortable-item').index($(this).parent());
					let sortable_items = $('tbody tr.sortable-item');

					let b_sortable_items = $('tbody tr.sortable-item').slice(0, current_index).get().reverse();
					let a_sortable_items = $('tbody tr.sortable-item').slice(current_index + 1, sortable_items.length).get();

					fnSelectedItemsAddClass(b_sortable_items, a_sortable_items, current_item);
				});

				// ----------  科目のマウスダウンイベント  ----------
				$('.sortable-item-handler').on('mousedown', function() {
					$('.sortable-tbody').find('.selected').each(function(index, data) {
						$(data).removeClass('selected');
					});
					let current_item = $(this).parent();
					$(current_item).addClass('selected');
				});

				// ----------  フォームイベント  ----------
				$('.s-name').on('change', function() {
					let val = $(this).val();
					$(this).attr('value', val);
				});
				$('.ss-name').on('change', function() {
					let val = $(this).val();
					$(this).attr('value', val);
				});
				$('.display-flg').on('change', function() {
					let is_checked = $(this).prop('checked');
					if (is_checked) {
						$(this).attr('value', 0);
					} else {
						$(this).attr('value', 1);
					}
				});
				$('input#checkDisplayFlg').on('change', function() {
					let is_checked = $(this).prop('checked');
					$('.display-flg-style .display-flg').prop('checked', is_checked);
					$('.display-flg').each(function() {
						if (is_checked) {
							$(this).attr('value', 0);
						} else {
							$(this).attr('value', 1);
						}
					});
				});
			}
		});
	</script>

Controller

array_multisort( ソートの軸となる配列(1), 並び順(1), ソートするデータの型(1), ソートの軸となる配列(2), 並び順(2), ソートするデータの型(2), ソートしたい配列);

public function detail()
	{
		$school_id = $this->login_data['school_id'];
		$school_category_id = $this->school['school_category_id'];

		// 指導要領
		$curriculum_guidelines_tmp = $this->MCurriculumGuidelines_m->getCurriculumGuidelinesBySchoolCategoryId($school_category_id);
		// 中高一貫校の場合、school_category_idを複数もつ場合があるため、それに対応
		$curriculum_guidelines = array();
		foreach ($curriculum_guidelines_tmp as $tmp) {
			$curriculum_guidelines[$tmp['school_category_id']][$tmp['id']] = $tmp;
		}
		$curriculum_guideline_ids = array_keys($curriculum_guidelines[$school_category_id]);
		$curriculum_guideline_id = null;
		if ($this->input->get('curriculum_guideline_type')) {
			$curriculum_guideline_id = $this->input->get('curriculum_guideline_type');
		}

		if (is_null($curriculum_guideline_id)) {
			$curriculum_guideline_id = end($curriculum_guideline_ids);
		}

		if (!in_array($curriculum_guideline_id, $curriculum_guideline_ids)) {
			show_error("不正な値が送信されました。", 404);
		}

		$report_type_array = $this->SchoolReportSubjectReplaceSetting_m->getList($school_id);

		$m_report_type_array = $this->MSchoolReportSubjectReplaceSetting_m->getList($school_id);
		foreach ($report_type_array as $key_index => $report_type) {
			if (in_array($report_type['report_type'], array_column($m_report_type_array, 'report_type'))) {
				$s_key_index = array_search($report_type['report_type'], array_column($m_report_type_array, 'report_type'));
				$report_type_array[$key_index]['report_type_name'] = $m_report_type_array[$s_key_index]['report_type_name'];
			}
		}
		$school_report_subject_replace_all = [];
		foreach ($report_type_array as $key => $reportType) {
			$school_subjects = $this->getSchoolSubjects($school_id);
			$sub_subjects_array = $this->getUsingSubSubjects($school_id, $school_category_id, $curriculum_guideline_id, $school_subjects);
			$sub_subjects_list = array_column($sub_subjects_array, null, 'sub_subject_id');
			$school_report_subject_replace_all[$reportType['report_type']] = $sub_subjects_list;
		}

		$school_report_subject_replace_by_report_type = [];
		foreach ($report_type_array as $key => $reportType) {
			$school_report_subject_replace_array = $this->getSchoolReportSubjectReplaces($school_id, $reportType['report_type']);
			if (empty($school_report_subject_replace_array)) {
				// 対象のレポートタイプのレコードが空の場合、commonで取得
				$school_report_subject_replace_array = $this->getSchoolReportSubjectReplaces($school_id, 'common');
			}
			$school_report_subject_replace_array_column_list = array_column($school_report_subject_replace_array, null, 'sub_subject_id');
			$school_report_subject_replace_by_report_type[$reportType['report_type']] = $school_report_subject_replace_array_column_list;
		}

		foreach ($school_report_subject_replace_all as $rt_key => $school_report_subject_replace) {
			foreach ($school_report_subject_replace as $ss_key => $sub_subjects) {
				if (empty($school_report_subject_replace_all[$rt_key][$ss_key])) {
					continue;
				} else {
					if (empty($school_report_subject_replace_by_report_type[$rt_key][$ss_key])) {
						$school_report_subject_replace_all[$rt_key][$ss_key]['report_type'] = $rt_key;
						$school_report_subject_replace_all[$rt_key][$ss_key]['s_name'] = $school_report_subject_replace_all[$rt_key][$ss_key]['subject_name'];
						$school_report_subject_replace_all[$rt_key][$ss_key]['ss_name'] = $school_report_subject_replace_all[$rt_key][$ss_key]['sub_subject_name'];
						$school_report_subject_replace_all[$rt_key][$ss_key]['s_r_sort_no'] = null;
						$school_report_subject_replace_all[$rt_key][$ss_key]['display_flg'] = 1;
					} else {
						$school_report_subject_replace_all[$rt_key][$ss_key]['report_type'] = $rt_key;
						$school_report_subject_replace_all[$rt_key][$ss_key]['s_name'] = $school_report_subject_replace_by_report_type[$rt_key][$ss_key]['s_name'];
						$school_report_subject_replace_all[$rt_key][$ss_key]['ss_name'] = $school_report_subject_replace_by_report_type[$rt_key][$ss_key]['ss_name'];
						$school_report_subject_replace_all[$rt_key][$ss_key]['s_r_sort_no'] = $school_report_subject_replace_by_report_type[$rt_key][$ss_key]['s_r_sort_no'];
						$school_report_subject_replace_all[$rt_key][$ss_key]['display_flg'] = $school_report_subject_replace_by_report_type[$rt_key][$ss_key]['display_flg'];
					}
				}
			}
		}

		foreach ($school_report_subject_replace_all as $rt_key => $school_report_subject_replace) {
			// ソートに対応して整形
			if (!in_array(null, array_column($school_report_subject_replace, 's_r_sort_no'))) {
				// 2回目以降表示(school_report_subject_replace_arrayのデータが不足していない場合)
				$srsr_sort_no_list = array_map([$this, 'castInt'], array_column($school_report_subject_replace, 's_r_sort_no'));
				array_multisort($srsr_sort_no_list, SORT_ASC, SORT_NUMERIC, $school_report_subject_replace_all[$rt_key]);
			} else {
				// 初回表示(デフォルト表示のソート)
				$s_sort_no_list = array_map([$this, 'castInt'], array_column($school_report_subject_replace, 's_sort_no'));
				$ss_sort_no_list = array_map([$this, 'castInt'], array_column($school_report_subject_replace, 's_s_sort_no'));
				array_multisort($s_sort_no_list, SORT_ASC, SORT_NUMERIC, $ss_sort_no_list, SORT_ASC, SORT_NUMERIC, $school_report_subject_replace_all[$rt_key]);
			}
		}

		$body_class_append = 'subjects';
		$data = array(
			'body_id'             => 'admin',
			'body_class'          => 'admin school ' . $body_class_append,
			'school'              => $this->school,
			'report_type_array' => $report_type_array,
			'school_report_subject_replace_array' => $school_report_subject_replace_all,
			'curriculum_guideline_id' => $curriculum_guideline_id,
			'curriculum_guidelines' => $curriculum_guidelines[$tmp['school_category_id']],
		);

		$data += $this->login_data;

		$this->addBreadList('教科・科目表示詳細設定', '/subject_list/detail');

		$this->parser->parse('header',           $data);
		$this->parser->parse('school/subject_setting/detail', $data);
		$this->parser->parse('footer',           $data);
	}

	public function edit()
	{
		$school_id = $this->login_data['school_id'];
		$school_category_id = $this->school['school_category_id'];

		// 指導要領
		$curriculum_guidelines_tmp = $this->MCurriculumGuidelines_m->getCurriculumGuidelinesBySchoolCategoryId($school_category_id);
		// 中高一貫校の場合、school_category_idを複数もつ場合があるため、それに対応
		$curriculum_guidelines = array();
		foreach ($curriculum_guidelines_tmp as $tmp) {
			$curriculum_guidelines[$tmp['school_category_id']][$tmp['id']] = $tmp;
		}
		$curriculum_guideline_ids = array_keys($curriculum_guidelines[$school_category_id]);
		$curriculum_guideline_id = null;
		if ($this->input->get('curriculum_guideline_type')) {
			$curriculum_guideline_id = $this->input->get('curriculum_guideline_type');
		}

		if (is_null($curriculum_guideline_id)) {
			$curriculum_guideline_id = end($curriculum_guideline_ids);
		}

		if (!in_array($curriculum_guideline_id, $curriculum_guideline_ids)) {
			show_error("不正な値が送信されました。", 404);
		}

		$report_type_array = $this->SchoolReportSubjectReplaceSetting_m->getList($school_id);

		$m_report_type_array = $this->MSchoolReportSubjectReplaceSetting_m->getList($school_id);

		$report_type_column = array();
		foreach ($report_type_array as $key_index => $report_type) {
			if (in_array($report_type['report_type'], array_column($m_report_type_array, 'report_type'))) {
				$s_key_index = array_search($report_type['report_type'], array_column($m_report_type_array, 'report_type'));
				$report_type_array[$key_index]['report_type_name'] = $m_report_type_array[$s_key_index]['report_type_name'];
				$report_type_column[$report_type['report_type']]['report_type_name'] = $m_report_type_array[$s_key_index]['report_type_name'];
			}
		}

		// クエリパラメータが付いてリクエスト
		if (!is_null($this->input->get('report_type')) && in_array($this->input->get('report_type'), array_column($report_type_array, 'report_type'))) {
			$report_type = $this->input->get('report_type');
			$report_type_name = $report_type_column[$report_type]['report_type_name'];
		} else {
			if ($this->input->get('report_type') === 'common') {
				$report_type = 'common';
				$report_type_name = '一括';
			} else {
				show_error('urlに無効なパラメータが入っています。', 404, 'アクセスできません。');
				return;
			}
		}

		$school_report_subject_replace_all = [];
		$school_subjects = $this->getSchoolSubjects($school_id);
		$sub_subjects_array = $this->getUsingSubSubjects($school_id, $school_category_id, $curriculum_guideline_id, $school_subjects);
		$sub_subjects_list = array_column($sub_subjects_array, null, 'sub_subject_id');
		$school_report_subject_replace_all = $sub_subjects_list;

		// 帳票科目の取得
		$school_report_subject_replace_array = $this->getSchoolReportSubjectReplaces($school_id, $report_type);
		if (empty($school_report_subject_replace_array)) {
			// 対象のレポートタイプのレコードが空の場合、commonで取得
			$school_report_subject_replace_array = $this->getSchoolReportSubjectReplaces($school_id, 'common');
		}
		$school_report_subject_replace_array_column_list = array_column($school_report_subject_replace_array, null, 'sub_subject_id');

		foreach ($school_report_subject_replace_all as $ss_key => $sub_subject) {
			if (empty($school_report_subject_replace_all[$ss_key])) {
				continue;
			} else {
				if (empty($school_report_subject_replace_array_column_list[$ss_key])) {
					$school_report_subject_replace_all[$ss_key]['report_type'] = $report_type;
					$school_report_subject_replace_all[$ss_key]['s_name'] = $sub_subject['subject_name'];
					$school_report_subject_replace_all[$ss_key]['ss_name'] = $sub_subject['sub_subject_name'];
					$school_report_subject_replace_all[$ss_key]['s_r_sort_no'] = null;
					$school_report_subject_replace_all[$ss_key]['display_flg'] = 1;
				} else {
					$school_report_subject_replace_all[$ss_key]['report_type'] = $report_type;
					$school_report_subject_replace_all[$ss_key]['s_name'] = $school_report_subject_replace_array_column_list[$ss_key]['s_name'];
					$school_report_subject_replace_all[$ss_key]['ss_name'] = $school_report_subject_replace_array_column_list[$ss_key]['ss_name'];
					$school_report_subject_replace_all[$ss_key]['s_r_sort_no'] = $school_report_subject_replace_array_column_list[$ss_key]['s_r_sort_no'];
					$school_report_subject_replace_all[$ss_key]['display_flg'] = $school_report_subject_replace_array_column_list[$ss_key]['display_flg'];
				}
			}
		}

		// // ソートに対応して整形
		if (!in_array(null, array_column($school_report_subject_replace_all, 's_r_sort_no'))) {
			// school_report_subject_replaceにレコードが存在する場合
			$srsr_sort_no_list = array_map([$this, 'castInt'], array_column($school_report_subject_replace_all, 's_r_sort_no'));
			array_multisort($srsr_sort_no_list, SORT_ASC, SORT_NUMERIC, $school_report_subject_replace_all);
		} else {
			// school_report_subject_replaceにレコードがない場合
			$s_sort_no_list = array_map([$this, 'castInt'], array_column($school_report_subject_replace_all, 's_sort_no'));
			$ss_sort_no_list = array_map([$this, 'castInt'], array_column($school_report_subject_replace_all, 's_s_sort_no'));
			array_multisort($s_sort_no_list, SORT_ASC, SORT_NUMERIC, $ss_sort_no_list, SORT_ASC, SORT_NUMERIC, $school_report_subject_replace_all);
		}

		$body_class_append = 'subjects';
		$data = array(
			'body_id'             => 'admin',
			'body_class'          => 'admin school ' . $body_class_append,
			'school'              => $this->school,
			'report_type'		  => $report_type,
			'report_type_name'		  => $report_type_name,
			'school_report_subject_replace_array' => $school_report_subject_replace_all,
			'curriculum_guideline_id' => $curriculum_guideline_id,
			'curriculum_guideline_name' => $curriculum_guidelines[$tmp['school_category_id']][$curriculum_guideline_id]['name']
		);

		$data += $this->login_data;

		$this->addBreadList('教科・科目詳細設定('.$report_type_name.')', '/subject_list/detail');

		$this->parser->parse('header',           $data);
		$this->parser->parse('school/subject_setting/edit', $data);
		$this->parser->parse('footer',           $data);
	}

	public function update()
	{
		$post_data =  json_decode($this->input->post('school_report_subject_replaces'), true);
		$report_type = $this->input->post('report_type');
		$report_type_name = $this->input->post('report_type_name');
		$curriculum_guideline_id = $this->input->post('curriculum_guideline_type');
		$school_id = $this->login_data['school_id'];
		$year = $this->login_data['year'];

		//***** トランザクション開始 *****
		$this->db->trans_start();
		if ($report_type === 'common') {
			foreach ($post_data as $ss_key => $data) {
				$this->db->where('school_id', $school_id);
				$this->db->where('sub_subject_id', $ss_key);
				$delete_status = $this->db->delete('school_report_subject_replace');
			}
		} else {
			foreach ($post_data as $ss_key => $data) {
				$this->db->where('school_id', $school_id);
				$this->db->where('report_type', $report_type);
				$this->db->where('sub_subject_id', $ss_key);
				$delete_status = $this->db->delete('school_report_subject_replace');
			}
		}

		foreach ($post_data as $data) {
			$create_data = array(
				'school_id' => $school_id,
				'report_type' => $report_type,
				'sub_subject_id' => $data['sub_subject_id'],
				's_name' => $data['s_name'],
				'ss_name' => $data['ss_name'],
				'sort_no' => $data['sort_no'],
				'display_flg' => $data['display_flg'],
				'year' => $year,
				'created' => $this->login_data['teacher_id'],
				'updated' => $this->login_data['teacher_id'],
			);
			$insert_status = $this->db->insert('school_report_subject_replace', $create_data, true);
		}
		if ($this->db->trans_status() !== false && $delete_status && $insert_status) {
			$this->db->trans_complete();
			$this->db->trans_commit();
		} else {
			$this->db->trans_rollback();
			$flash = array(
				'message' => 'データベースへの更新に失敗しました。',
				'status' => 'info-alert',
			);
			$this->session->set_flashdata($flash);
			redirect('/admin/school/subject_setting/edit?curriculum_guideline_type=' . $curriculum_guideline_id . '&report_type=' . $report_type);
		}
		//***** トランザクション終了 *****
		$flash = array(
			'message' => $report_type_name . 'の表示設定が完了しました。',
			'status' => 'info-alert',
		);
		$this->session->set_flashdata($flash);

		redirect('/admin/school/subject_setting/detail/');
	}

コメント

タイトルとURLをコピーしました