概要
ページ管理のページ一覧(/manage/page)で、一部のページ行が画面に表示されません。同じページがページ権限一覧(/manage/page/roleList)には正常に表示されるため、データが失われているわけではなく表示側の問題です。顧客問い合わせから発覚しました。
混入元: PR #2417「[ページ管理] ページ一覧で親ページごとに折り畳めるようにしました」(マージコミット 7040aa20f)
影響バージョン: v1.43.0 以降すべて(v1.44.0 を含む)
原因
PR #2417 で追加された resources/js/manage/page/index.js の isRowVisible() が、行の表示可否を data-parent-id(pages.parent_id カラム)を1段ずつ遡る方式で判定しています。
function isRowVisible(row, rowMap) {
let parentId = row.dataset.parentId;
while (parentId) {
const parentRow = rowMap.get(parentId);
if (!parentRow || parentRow.dataset.treeExpanded !== '1') {
return false; // ← 親行が見つからないだけで非表示になる
}
parentId = parentRow.dataset.parentId;
}
return true;
}
pages.parent_id は既に存在しないページIDを指している場合があります(根本原因は別Issueで報告)。その場合 rowMap.get(parentId) が undefined となり、!parentRow で即 false(非表示)になります。
一方、同じファイルの applyPreloadStyle() は Nested Set(_lft/_rgt)由来の data-ancestor-ids を使っており、こちらは正しく動作します。同一ファイル内で祖先情報のデータソースが2種類混在している点が問題の本質です。
補足:折り畳み状態とは無関係に常時発生します
init() は保存済みの折り畳み状態の有無にかかわらず updateVisibility() を無条件に実行するため、以下いずれの条件でも再現します。
- sessionStorage をクリアしても再現
- 別ブラウザ・シークレットウィンドウでも再現
- 全ユーザー・全端末で恒常的に発生
影響範囲
- ページ一覧に該当ページが出ないため、編集・権限設定・ページ移動などの操作導線が完全に断たれます
- ページ権限一覧など他の画面は影響を受けません
- 該当ページのフロント表示(一般利用者から見たサイト)には影響しません
実行環境
OS: 影響なし(クライアントサイドJSの問題のため全環境で再現)
ブラウザ: 影響なし
Connect-CMS: v1.43.0 以降(v1.44.0 で確認)
再現手順
- 階層構造を持つページを用意する(親ページA > 子ページB)
- ページ管理から親ページAを削除する(子ページBは削除されず残り、
parent_id が存在しないIDを指した状態になる)
- ページ一覧(
/manage/page)を開く
- 子ページBが一覧に表示されない
- ページ権限一覧(
/manage/page/roleList)を開くと、子ページBは表示される
修正方針
isRowVisible() の判定を、applyPreloadStyle() と同じ data-ancestor-ids ベースに統一します。data-ancestor-ids は page.blade.php で既に出力済みのため、サーバ側の変更は不要です。
function isRowVisible(row, rowMap) {
const ancestorIds = (row.dataset.ancestorIds || '').split(' ').filter(Boolean);
// 祖先が一覧内に無い場合は隠さず表示する(表示漏れを防ぐ)。
return ancestorIds.every(function (ancestorId) {
const ancestorRow = rowMap.get(ancestorId);
return !ancestorRow || ancestorRow.dataset.treeExpanded === '1';
});
}
祖先行が見つからない場合は「表示する」側(fail-safe)に倒します。本Issueは「祖先が引けないと消える」ことそのものが問題のため、同種の不整合が別経路で起きても表示漏れにならないようにする意図です。
副次的に、parent_id に循環参照があった場合の while 無限ループリスクも構造的に解消されます。
残課題(本Issueのスコープ外)
page.blade.php の $page_children = $pages->groupBy('parent_id') による has_children 判定も同じく parent_id ベースです。ある親の子が全員不整合な parent_id を持つ場合、その親に折り畳みボタンが表示されません。表示消失そのものは上記修正で解消するため別途対応とします。
概要
ページ管理のページ一覧(
/manage/page)で、一部のページ行が画面に表示されません。同じページがページ権限一覧(/manage/page/roleList)には正常に表示されるため、データが失われているわけではなく表示側の問題です。顧客問い合わせから発覚しました。混入元: PR #2417「[ページ管理] ページ一覧で親ページごとに折り畳めるようにしました」(マージコミット
7040aa20f)影響バージョン: v1.43.0 以降すべて(v1.44.0 を含む)
原因
PR #2417 で追加された
resources/js/manage/page/index.jsのisRowVisible()が、行の表示可否をdata-parent-id(pages.parent_idカラム)を1段ずつ遡る方式で判定しています。pages.parent_idは既に存在しないページIDを指している場合があります(根本原因は別Issueで報告)。その場合rowMap.get(parentId)がundefinedとなり、!parentRowで即false(非表示)になります。一方、同じファイルの
applyPreloadStyle()は Nested Set(_lft/_rgt)由来のdata-ancestor-idsを使っており、こちらは正しく動作します。同一ファイル内で祖先情報のデータソースが2種類混在している点が問題の本質です。補足:折り畳み状態とは無関係に常時発生します
init()は保存済みの折り畳み状態の有無にかかわらずupdateVisibility()を無条件に実行するため、以下いずれの条件でも再現します。影響範囲
実行環境
再現手順
parent_idが存在しないIDを指した状態になる)/manage/page)を開く/manage/page/roleList)を開くと、子ページBは表示される修正方針
isRowVisible()の判定を、applyPreloadStyle()と同じdata-ancestor-idsベースに統一します。data-ancestor-idsはpage.blade.phpで既に出力済みのため、サーバ側の変更は不要です。祖先行が見つからない場合は「表示する」側(fail-safe)に倒します。本Issueは「祖先が引けないと消える」ことそのものが問題のため、同種の不整合が別経路で起きても表示漏れにならないようにする意図です。
副次的に、
parent_idに循環参照があった場合のwhile無限ループリスクも構造的に解消されます。残課題(本Issueのスコープ外)
page.blade.phpの$page_children = $pages->groupBy('parent_id')によるhas_children判定も同じくparent_idベースです。ある親の子が全員不整合なparent_idを持つ場合、その親に折り畳みボタンが表示されません。表示消失そのものは上記修正で解消するため別途対応とします。