Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
0134947
add: ユーザ管理 項目の詳細設定に条件付き表示機能を追加
Oct 30, 2025
d0223a0
test: ユーザ管理 項目の詳細設定 条件付き表示テスト
Oct 30, 2025
fdee671
bug: ユーザ管理 項目詳細設定 トリガー項目にシステム項目が表示されない
Oct 31, 2025
09c984c
bug: ユーザ登録フォーム トリガー項目にシステム項目を設定しても条件付き表示が動作しない
Oct 31, 2025
27560c3
improve: ユーザ管理 項目詳細設定 表示する条件の記載テキストを調整
Oct 31, 2025
983a243
improve: ユーザ管理 項目詳細設定 表示する条件の記載テキストを調整
Oct 31, 2025
de2ab85
improve: ユーザ管理 項目詳細設定 設定状況の解説テキストを調整
Oct 31, 2025
f5c946a
bug: ユーザ管理 項目詳細設定 トリガーとなる項目から登録日時等、表示されない項目を除外
Oct 31, 2025
5fe78ed
improve: ユーザ登録フォーム 表示/非表示のアニメーションを滑らかに
Oct 31, 2025
8dbccef
test: ユーザ管理 条件付き表示テスト
Oct 31, 2025
12e4628
bug: ユーザ登録フォーム 未入力である/未入力でない、が機能していない
Oct 31, 2025
cefeab2
bug: ユーザ管理 条件付き表示 N+1対策
Oct 31, 2025
963af6c
improve: ユーザ管理 更新メッセージの生成ロジックを改善
Oct 31, 2025
79d0c22
improve: ユーザ管理 条件付き表示のトリガー項目ID取得ロジックを最適化
Oct 31, 2025
79a33e5
improve: ユーザ管理 トリガー項目の削除時にエラーメッセージをHTMLエスケープ
Oct 31, 2025
aa592dd
test: 条件付き表示機能のテストを追加し、ビジネスロジックとデータ整合性を確認
Oct 31, 2025
90b49dc
improve: ユーザ管理 項目詳細設定 登録時、循環参照チェックを追加
Oct 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions app/Enums/ConditionalOperator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Enums;

/**
* 条件演算子
*/
class ConditionalOperator extends EnumsBase
{
// 定数メンバ
const equals = 'equals';
const not_equals = 'not_equals';
const is_empty = 'is_empty';
const is_not_empty = 'is_not_empty';

// key/valueの連想配列
const enum = [
self::equals => '次の値と一致する',
self::not_equals => '次の値と一致しない',
self::is_empty => '未入力である',
self::is_not_empty => '未入力でない',
];
}
6 changes: 5 additions & 1 deletion app/Http/Controllers/Auth/RegistersUsers.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ trait RegistersUsers

/**
* Show the application registration form.
* ユーザー登録画面表示自動登録
* ユーザー登録画面表示(自動登録)
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
Expand Down Expand Up @@ -99,6 +99,9 @@ public function showRegistrationForm(Request $request)
// カラムの登録データ
$input_cols = null;

// 条件付き表示の設定を取得
$conditional_display_settings = UsersTool::getConditionalDisplaySettings($columns_set_id);

// サイトテーマ詰込
$tmp_configs = Configs::getSharedConfigs();
$base_theme = Configs::getConfigsValue($tmp_configs, 'base_theme', null);
Expand All @@ -119,6 +122,7 @@ public function showRegistrationForm(Request $request)
'users_columns' => $users_columns,
'users_columns_id_select' => $users_columns_id_select,
'input_cols' => $input_cols,
'conditional_display_settings' => $conditional_display_settings,
'themes' => $themes,
'sections' => Section::orderBy('display_sequence')->get(),
'user_section' => new UserSection(),
Expand Down
4 changes: 4 additions & 0 deletions app/Models/Core/UsersColumns.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class UsersColumns extends Model
'rule_regex',
'rule_word_count',
'display_sequence',
'conditional_display_flag',
'conditional_trigger_column_id',
'conditional_operator',
'conditional_value',
];

/**
Expand Down
146 changes: 133 additions & 13 deletions app/Plugins/Manage/UserManage/UserManage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Plugins\Manage\UserManage;

use App\Enums\ConditionalOperator;
use App\Enums\CsvCharacterCode;
use App\Enums\EditType;
use App\Enums\Required;
Expand Down Expand Up @@ -2635,6 +2636,14 @@ public function editColumns($request, $id)
// ユーザーのカラム
$columns = UsersTool::getUsersColumns($id);

// トリガー項目として使用されている項目IDを取得
$trigger_column_ids = UsersColumns::where('columns_set_id', $id)
->where('conditional_display_flag', ShowType::show)
->whereNotNull('conditional_trigger_column_id')
->distinct()
->pluck('conditional_trigger_column_id')
->toArray();

foreach ($columns as &$column) {
if (UsersColumns::isSelectColumnType($column->column_type)) {
// 選択肢
Expand All @@ -2646,6 +2655,9 @@ public function editColumns($request, $id)
} else {
$column->selects = collect();
}

// トリガー項目として使用されているかのフラグを追加
$column->is_used_as_trigger = in_array($column->id, $trigger_column_ids);
}

return view('plugins.manage.user.edit_columns', [
Expand Down Expand Up @@ -2741,23 +2753,39 @@ public function updateColumn($request, $id)
$column->column_type = $request->$str_column_type;
$message = '項目【 '. $column->column_name .' 】を更新しました。';

$messages = [];

if (UsersColumns::isShowOnlyColumnType($column->column_type)) {
$column->required = Required::off;
$message = '項目【 '.$column->column_name.' 】を更新し、表示のみの型のため、必須入力を【 off 】に設定しました';
$messages[] = '表示のみの型のため、必須入力を【 off 】に設定しました';
} else {
// 通常
$column->required = $request->$str_required ? Required::on : Required::off;

// 必須ONに変更した場合、条件付き表示をOFFにする
if ($column->required == Required::on && $column->conditional_display_flag == ShowType::show) {
$column->conditional_display_flag = ShowType::not_show;
$column->conditional_trigger_column_id = null;
$column->conditional_operator = null;
$column->conditional_value = null;
$messages[] = '必須入力ONのため、条件付き表示を【 OFF 】に設定しました';
}
}

// 固定項目以外
if (!UsersColumns::isFixedColumnType($column->column_type)) {
// 必須入力
if ($column->required == Required::on) {
$column->is_show_auto_regist = ShowType::show;
$message = '項目【 '.$column->column_name.' 】を更新し、必須入力のため、自動登録時の表示指定【 '.ShowType::getDescription($column->is_show_auto_regist).' 】を設定しました';
$messages[] = '必須入力のため、自動登録時の表示指定【 '.ShowType::getDescription($column->is_show_auto_regist).' 】を設定しました';
}
}

// メッセージの生成
if (!empty($messages)) {
$message = '項目【 '.$column->column_name.' 】を更新し、' . implode('。また、', $messages) . '。';
}

$column->save();

// 編集画面を呼び出す
Expand Down Expand Up @@ -2835,8 +2863,30 @@ public function deleteColumn($request, $id)
// 明細行から削除対象の項目名を抽出
$str_column_name = "column_name_"."$request->column_id";

// 所属型の関連テーブルを削除
$users_column = UsersColumns::findOrFail($request->column_id);

// この項目をトリガーにしている項目がないかチェック
$dependent_columns = UsersColumns::where('columns_set_id', $request->columns_set_id)
->where('conditional_display_flag', ShowType::show)
->where('conditional_trigger_column_id', $request->column_id)
->get();

if ($dependent_columns->count() > 0) {
// トリガーとして使用されている場合は削除不可(HTMLエスケープ)
$dependent_names_escaped = $dependent_columns->pluck('column_name')
->map(function ($name) {
return e($name);
})
->toArray();

$error_message = '項目【 '. e($request->$str_column_name) .' 】は以下の項目のトリガーとして使用されているため削除できません。<br>';
$error_message .= '先に以下の項目の条件付き表示をOFFにしてから削除してください。<br>';
$error_message .= '・' . implode('<br>・', $dependent_names_escaped);

return redirect()->back()->with('errors_flash_message', $error_message);
}

// 所属型の関連テーブルを削除
if ($users_column->column_type === UserColumnType::affiliation) {
UserSection::query()->delete();
Section::query()->delete();
Expand Down Expand Up @@ -2879,14 +2929,24 @@ public function editColumnDetail($request, $id)
$selects = UsersColumnsSelects::where('users_columns_id', $column->id)->orderby('display_sequence')->get();
$select_agree = $selects->first() ?? new UsersColumnsSelects();

// トリガー候補の項目を取得
// 条件:自分自身を除く(システム固定項目・カスタム必須項目も含める)
// ただし、登録フォームに表示されない項目(登録日時、更新日時)は除外
$trigger_columns = UsersColumns::where('columns_set_id', $column->columns_set_id)
->where('id', '!=', $id) // 自分自身を除外
->whereNotIn('column_type', UserColumnType::showOnlyColumnTypes())
->orderBy('display_sequence')
->get();

return view('plugins.manage.user.edit_column_detail', [
"function" => __FUNCTION__,
"plugin_name" => "user",
'columns_set' => $columns_set,
'column' => $column,
'selects' => $selects,
'select_agree' => $select_agree,
'sections' => Section::orderBy('display_sequence')->get(),
"function" => __FUNCTION__,
"plugin_name" => "user",
'columns_set' => $columns_set,
'column' => $column,
'selects' => $selects,
'select_agree' => $select_agree,
'sections' => Section::orderBy('display_sequence')->get(),
'trigger_columns' => $trigger_columns,
]);
}

Expand Down Expand Up @@ -2932,6 +2992,55 @@ public function updateColumnDetail($request, $id)
$validator_attributes['variable_name'] = '変数名';
}

// カラム取得
$column = UsersColumns::where('id', $request->column_id)->where('columns_set_id', $request->columns_set_id)->first();
if (!$column) {
abort(404, 'カラムデータがありません。');
}

// システム固定項目または必須項目は条件付き表示を設定できない
if (UsersColumns::isFixedColumnType($column->column_type) || $column->required == Required::on) {
// 強制的に条件付き表示をOFFにする
$request->merge(['conditional_display_flag' => ShowType::not_show]);
}

// 条件付き表示のバリデーション
if ($request->conditional_display_flag == ShowType::show) {
$validator_values['conditional_trigger_column_id'] = ['required'];
$validator_values['conditional_operator'] = ['required'];

// 空白チェック(is_empty, is_not_empty)以外の場合のみ条件の値を必須にする
if ($request->conditional_operator !== ConditionalOperator::is_empty &&
$request->conditional_operator !== ConditionalOperator::is_not_empty) {
$validator_values['conditional_value'] = ['required', 'string', 'max:255'];
}

$validator_attributes['conditional_trigger_column_id'] = 'トリガーとなる項目';
$validator_attributes['conditional_operator'] = '表示する条件';
$validator_attributes['conditional_value'] = '条件の値';

// トリガー項目の追加バリデーション
$validator_values['conditional_trigger_column_id'][] = function ($attribute, $value, $fail) use ($column) {
if ($value) {
$trigger_column = UsersColumns::find($value);
if ($trigger_column) {
// 自分自身をトリガーにできない
if ($trigger_column->id == $column->id) {
$fail('トリガーとなる項目に自分自身は設定できません。');
}
// 同じ項目セットに属していることを確認
if ($trigger_column->columns_set_id != $column->columns_set_id) {
$fail('トリガーとなる項目は同じ項目セットに属している必要があります。');
}
// 循環依存チェック(A→B→C→Aのような循環参照を防止)
if (UsersTool::hasCyclicDependency($column->id, $value, $column->columns_set_id)) {
$fail('この設定により循環依存が発生します。トリガーとなる項目の依存関係を確認してください。');
}
}
}
};
}

// エラーチェック
if ($validator_values) {
$validator = Validator::make($request->all(), $validator_values);
Expand All @@ -2942,9 +3051,6 @@ public function updateColumnDetail($request, $id)
}
}


$column = UsersColumns::where('id', $request->column_id)->where('columns_set_id', $request->columns_set_id)->first();

// 項目の更新処理
$column->caption = $request->caption;
if ($request->caption_color) {
Expand Down Expand Up @@ -2973,6 +3079,20 @@ public function updateColumnDetail($request, $id)
// 正規表現
$column->rule_regex = $request->rule_regex;

// 条件付き表示設定の更新
$column->conditional_display_flag = $request->conditional_display_flag ?? ShowType::not_show;

if ($column->conditional_display_flag == ShowType::show) {
$column->conditional_trigger_column_id = $request->conditional_trigger_column_id;
$column->conditional_operator = $request->conditional_operator;
$column->conditional_value = $request->conditional_value;
} else {
// OFFの場合はクリア
$column->conditional_trigger_column_id = null;
$column->conditional_operator = null;
$column->conditional_value = null;
}

// 保存
$column->save();

Expand Down
93 changes: 93 additions & 0 deletions app/Plugins/Manage/UserManage/UsersTool.php
Original file line number Diff line number Diff line change
Expand Up @@ -429,4 +429,97 @@ private static function getOptionClass(): ?string
}
return null;
}

/**
* 条件付き表示の設定情報を取得
*
* @param int $columns_set_id カラムセットID
* @return array 条件付き表示の設定情報の配列
*/
public static function getConditionalDisplaySettings($columns_set_id)
{
$conditional_columns = UsersColumns::where('columns_set_id', $columns_set_id)
->where('conditional_display_flag', ShowType::show)
->whereNotNull('conditional_trigger_column_id')
->whereNotNull('conditional_operator')
->get();

// トリガー項目を一括取得(N+1クエリ対策)
$trigger_ids = $conditional_columns->pluck('conditional_trigger_column_id')->unique();
$trigger_columns = UsersColumns::whereIn('id', $trigger_ids)->get()->keyBy('id');

$settings = [];
foreach ($conditional_columns as $column) {
$trigger_column = $trigger_columns->get($column->conditional_trigger_column_id);

$settings[] = [
'target_column_id' => $column->id,
'trigger_column_id' => $column->conditional_trigger_column_id,
'trigger_column_type' => $trigger_column ? $trigger_column->column_type : null,
'operator' => $column->conditional_operator,
'value' => $column->conditional_value,
];
}

return $settings;
}

/**
* 循環依存をチェックする
*
* 指定された項目をトリガーに設定した場合、循環依存が発生しないかをチェックします。
* 例: A→B→C→A のような循環参照を検出
*
* @param int $column_id 条件付き表示を設定する項目のID
* @param int $trigger_column_id トリガーとして設定しようとしている項目のID
* @param int $columns_set_id 項目セットID
* @return bool 循環依存がある場合true、ない場合false
*/
public static function hasCyclicDependency($column_id, $trigger_column_id, $columns_set_id)
{
// トリガー項目が設定されていない場合は循環しない
if (empty($trigger_column_id)) {
return false;
}

// 訪問済みノードを記録(無限ループ防止)
$visited = [];

// 探索スタック(深さ優先探索)
$stack = [$trigger_column_id];

// 同一項目セット内の条件付き表示設定を一度に取得(パフォーマンス最適化)
$conditional_columns = UsersColumns::where('columns_set_id', $columns_set_id)
->where('conditional_display_flag', ShowType::show)
->whereNotNull('conditional_trigger_column_id')
->get()
->keyBy('id');

while (!empty($stack)) {
$current_id = array_pop($stack);

// 自分自身に到達したら循環依存を検出
if ($current_id == $column_id) {
return true;
}

// 既に訪問済みの場合はスキップ
if (in_array($current_id, $visited)) {
continue;
}

// 訪問済みとしてマーク
$visited[] = $current_id;

// 現在のノードがトリガーとして設定されているか確認
$current_column = $conditional_columns->get($current_id);
if ($current_column && $current_column->conditional_trigger_column_id) {
// 次のトリガーをスタックに追加
$stack[] = $current_column->conditional_trigger_column_id;
}
}

// 循環依存なし
return false;
}
}
Loading