prepare('SELECT sys_comp_code FROM edu_users WHERE member_id = ? ORDER BY sys_comp_code LIMIT 1'); $__st->execute([$memberId]); $sysCompCode = (string)($__st->fetchColumn() ?: ''); } catch (Throwable $__e) { /* ignore */ } } // --------------------------------------------------------- // 디버그 설정 // true : 바인딩 적용된 SQL 출력 후 종료 // false : 정상 실행 // --------------------------------------------------------- //https://baroncs.co.kr/ajax/get_video_list.php?SET_PREFIX=I $DEBUG_SQL = false;//정상진행 //$DEBUG_SQL = true; //디버깅 $prefixCondition = isset($SET_PREFIX) ? $SET_PREFIX : 'L'; //L=리더십(기본값), I=인사이트 // --------------------------------------------------------- // 요청 파라미터 // --------------------------------------------------------- $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; $limitParam = trim((string)($_GET['limit'] ?? '')); $limit = 8; $noLimit = false; if ($limitParam !== '') { if (strtolower($limitParam) === 'all') { $noLimit = true; } else { $parsedLimit = (int)$limitParam; if ($parsedLimit > 0) { $limit = $parsedLimit; } } } $offset = ($page - 1) * $limit; $category = trim($_GET['category'] ?? ''); // 예: CA200L01 $sort = trim($_GET['sort'] ?? 'view'); // view / latest / seen / unseen // --------------------------------------------------------- // 기본 코드맵 (DB코드 조회 실패 대비) // --------------------------------------------------------- if($prefixCondition=="L"){ $SET_category_code = "CA10004";//리더십 $CATEGORY_MAP = [ 'CA10004' => '리더십', ]; $SUBCATE_MAP = [ 'CA200L01' => '리더십 시작하기', 'CA200L02' => '셀프 리더십', 'CA200L03' => '팀 리더십', 'CA200L04' => '실전 리더십', 'CA200L05' => '인물탐구', ]; }else if($prefixCondition=="I"){ $SET_category_code = "CA10005";//인사이트 $CATEGORY_MAP = [ 'CA10005' => '인사이트', ]; $SUBCATE_MAP = [ 'CA200I01' => '경제와 사회', 'CA200I02' => '기술과 미래', 'CA200I03' => '트렌드 업', 'CA200I04' => '행복과 건강', 'CA200I05' => '피플스토리', 'CA200I06' => '라이프', ]; } // --------------------------------------------------------- // 공통 함수 // --------------------------------------------------------- function FN_change_entity(?string $str): string { return htmlspecialchars((string)$str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } function mapContentRow(array $row, array $catMap = [], array $subcateMap = []): array { $kwStr = $row['keyword_names'] ?? ($row['keywords'] ?? ''); $keywords = $kwStr !== '' ? explode(',', $kwStr) : []; $keywords = array_values(array_filter(array_map('trim', $keywords), static function ($v) { return $v !== ''; })); $watchTm = (float)($row['watch_tm'] ?? 0); $contentTm = (float)($row['content_tm'] ?? 0); $gauge = ($contentTm > 0) ? (int)round(($watchTm / $contentTm) * 100) : 0; $gauge = min(100, max(0, $gauge)); $raw = trim($row['content_url'] ?? ''); if (preg_match('/(?:v=|youtu\.be\/)([A-Za-z0-9_-]{11})/', $raw, $m)) { $videoId = $m[1]; } elseif (preg_match('/^[A-Za-z0-9_-]{11}$/', $raw)) { $videoId = $raw; } else { $videoId = ''; } $thumbFromDb = trim($row['thumbnail_url'] ?? ''); $url = $videoId !== '' ? "https://www.youtube.com/watch?v={$videoId}" : $raw; $thumbnail = $thumbFromDb !== '' ? $thumbFromDb : ($videoId !== '' ? "https://img.youtube.com/vi/{$videoId}/sddefault.jpg" : ''); $categoryCode = $row['category_code'] ?? ''; $groupCode = $row['category_group'] ?? ''; return [ 'id' => $row['content_id'] ?? '', 'url' => $url, 'thumbnail' => $thumbnail, 'category' => $catMap[$categoryCode] ?? $categoryCode, 'category_code' => $categoryCode, 'subcate' => $subcateMap[$groupCode] ?? $groupCode, 'bookmark' => !empty($row['is_bookmarked']), 'title' => $row['title'] ?? '', 'description' => $row['description'] ?? '', 'keywords' => $keywords, 'gauge' => $gauge, 'watch_tm' => (int)($row['watch_tm'] ?? 0), 'content_tm' => (int)($row['content_tm'] ?? 0), 'view_cnt' => (int)($row['view_cnt'] ?? 0), ]; } /** * 디버그용: 바인딩값을 SQL에 치환한 문자열 생성 * 주의: 실제 실행 SQL을 PDO가 제공하는 것은 아니고, 보기 쉽게 만든 디버그용 문자열임 */ function buildDebugSql(string $sql, array $params): string { // :member_id 와 :member_id2 같은 이름 충돌 방지 uksort($params, function ($a, $b) { return strlen((string)$b) <=> strlen((string)$a); }); foreach ($params as $key => $value) { if ($value === null) { $replace = 'NULL'; } elseif (is_int($value) || is_float($value)) { $replace = (string)$value; } else { $replace = "'" . str_replace("'", "''", (string)$value) . "'"; } $sql = str_replace($key, $replace, $sql); } return $sql; } try { $pdo = db_conn(); $pdo->exec("SET NAMES 'utf8mb4'"); // --------------------------------------------------------- // edu_codes 에서 실제 코드맵 조회 // --------------------------------------------------------- $stmtCode = $pdo->query(" SELECT group_code, code, code_name FROM edu_codes WHERE is_active = 1 AND ( group_code = 'CA100' OR ( group_code = 'CA200' AND code LIKE '".$prefixCondition."%' ) ) "); if ($stmtCode) { foreach ($stmtCode->fetchAll(PDO::FETCH_ASSOC) as $cr) { $key = $cr['group_code'] . $cr['code']; if ($cr['group_code'] === 'CA100') { $CATEGORY_MAP[$key] = $cr['code_name']; } elseif ($cr['group_code'] === 'CA200') { $SUBCATE_MAP[$key] = $cr['code_name']; } } } // --------------------------------------------------------- // 이 페이지는 리더십 대분류 전용 // 탭은 category_group 으로 필터링 // category 파라미터는 CA200L01 형태를 권장 // --------------------------------------------------------- $categoryGroup = ''; if ($category !== '' && strtolower($category) !== 'all') { if (preg_match('/^L\d{2}$/', $category)) { $categoryGroup = 'CA200' . $category; // L01 -> CA200L01 } else { $categoryGroup = $category; // 이미 CA200L01이면 그대로 } } // --------------------------------------------------------- // 정렬 조건 // --------------------------------------------------------- switch ($sort) { case 'latest': $orderBy = " c.created_at DESC, c.content_id DESC "; break; case 'seen': $orderBy = " c.created_at DESC, CASE WHEN lh.content_id IS NOT NULL THEN 0 ELSE 1 END ASC, lh.last_viewed_at DESC, c.content_id DESC "; break; case 'unseen': $orderBy = " c.created_at DESC, CASE WHEN lh.content_id IS NULL THEN 0 ELSE 1 END ASC, COALESCE(c.updated_at, c.created_at) DESC, c.content_id DESC "; break; case 'view': default: $orderBy = " COALESCE(vs.view_cnt, 0) DESC, COALESCE(vs.total_all_tm, 0) DESC, c.created_at DESC, c.content_id DESC "; break; } // --------------------------------------------------------- // 기본 조건 // --------------------------------------------------------- $where = []; $bind = []; //배열추가 $where[] = "c.is_active = '1'"; $where[] = "(c.start_date IS NULL OR c.start_date <= CURDATE())"; $where[] = "(c.end_date IS NULL OR c.end_date >= CURDATE())"; // 리더십 대분류 고정 $where[] = "c.category_code = '{$SET_category_code}'"; //edu_codes.base_code == edu_contents.category_code // 탭 필터 if ($categoryGroup !== '') { $where[] = "c.category_group = :category_group"; $bind[':category_group'] = $categoryGroup; } $whereSql = implode("\n AND ", $where);//배열 사이에 구분자를 넣어서 연결 // --------------------------------------------------------- // TOTAL COUNT // --------------------------------------------------------- $countSql = " SELECT COUNT(*) FROM edu_contents c WHERE {$whereSql} "; $stmtCount = $pdo->prepare($countSql); foreach ($bind as $key => $value) { $stmtCount->bindValue($key, $value, PDO::PARAM_STR); } if ($DEBUG_SQL) { $countDebugSql = buildDebugSql($countSql, $bind); echo '
';
echo "[COUNT SQL]\n\n";
// echo htmlspecialchars($countDebugSql, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
echo $countDebugSql;
echo "\n\n------------------------------\n\n";
}
$stmtCount->execute();
$totalCount = (int)$stmtCount->fetchColumn();
// ---------------------------------------------------------
// 목록 조회
// ---------------------------------------------------------
$limitSql = $noLimit ? '' : "\n LIMIT :limit OFFSET :offset\n";
$sql = "
SELECT
c.content_id,
c.category_code,
c.category_group,
c.title,
c.content_url,
c.thumbnail_url,
c.description,
c.sort_order,
c.created_at,
c.updated_at,
lh.content_id AS lh_content_id,
lh.watch_tm,
lh.content_tm,
lh.last_viewed_at,
CASE
WHEN cw.content_id IS NOT NULL THEN 1
ELSE 0
END AS is_bookmarked,
COALESCE(vs.view_cnt, 0) AS view_cnt,
COALESCE(vs.total_all_tm, 0) AS total_all_tm,
GROUP_CONCAT(
DISTINCT ck.keyword_code
ORDER BY ck.keyword_code
SEPARATOR ','
) AS keywords,
GROUP_CONCAT(
DISTINCT COALESCE(kc.code_name, ck.keyword_code)
ORDER BY ck.keyword_code
SEPARATOR ','
) AS keyword_names
FROM edu_contents c
LEFT JOIN edu_learning_histories lh
ON lh.content_id = c.content_id
AND lh.member_id = :member_id
AND lh.sys_comp_code = :sys_comp_code
LEFT JOIN edu_content_wishlist cw
ON cw.content_id = c.content_id
AND cw.member_id = :member_id2
AND cw.sys_comp_code = :sys_comp_code2
AND cw.is_active = '1'
LEFT JOIN (
SELECT
content_id,
COUNT(DISTINCT member_id) AS view_cnt,
COALESCE(SUM(all_tm), 0) AS total_all_tm
FROM edu_learning_histories
GROUP BY content_id
) vs
ON vs.content_id = c.content_id
LEFT JOIN edu_content_keywords ck
ON ck.content_id = c.content_id
AND ck.is_active = '1'
LEFT JOIN edu_codes kc
ON kc.base_code = ck.keyword_code
AND kc.is_active = 1
WHERE {$whereSql}
GROUP BY c.content_id
ORDER BY {$orderBy}
{$limitSql}
";
/*LIMIT :limit OFFSET :offset*/
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':member_id', $memberId, PDO::PARAM_STR);
$stmt->bindValue(':sys_comp_code', $sysCompCode, PDO::PARAM_STR);
$stmt->bindValue(':member_id2', $memberId, PDO::PARAM_STR);
$stmt->bindValue(':sys_comp_code2', $sysCompCode, PDO::PARAM_STR);
foreach ($bind as $key => $value) {
$stmt->bindValue($key, $value, PDO::PARAM_STR);
}
if (!$noLimit) {
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
}
if ($DEBUG_SQL) {
$debugParams = [
':member_id' => $memberId,
':sys_comp_code' => $sysCompCode,
':member_id2' => $memberId,
':sys_comp_code2' => $sysCompCode,
];
if (!$noLimit) {
$debugParams[':limit'] = $limit;
$debugParams[':offset'] = $offset;
}
if (!empty($bind)) {
$debugParams = array_merge($debugParams, $bind);
}
$debugSql = buildDebugSql($sql, $debugParams);
echo "[LIST SQL]\n\n";
//echo htmlspecialchars($debugSql, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
echo $debugSql;
echo '';
}
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($DEBUG_SQL) {
print_r($rows);
exit;
}
// ---------------------------------------------------------
// HTML 조각 생성
// ---------------------------------------------------------
ob_start();
foreach ($rows as $row) {
$item = mapContentRow($row, $CATEGORY_MAP, $SUBCATE_MAP);
$contentId = FN_change_entity($item['id']);
$title = FN_change_entity($item['title']);
$thumb = FN_change_entity($item['thumbnail']);
$categoryNm= FN_change_entity($item['category']);
$subcateNm = FN_change_entity($item['subcate']);
$descRaw = $item['description'] ?? '';
$keywords = $item['keywords'] ?? [];
$keywordsCsv = implode(',', $keywords);
$keywordTags = '';
if (!empty($keywords)) {
$keywordTags = implode(' ', array_map(static function ($kw) {
return '#' . $kw;
}, $keywords));
}
$itemDesc = $keywordTags !== ''
? FN_change_entity($keywordTags)
: ($subcateNm !== '' ? $subcateNm : $categoryNm);
$checked = $item['bookmark'] ? ' checked' : '';
$viewUrl = "/video_view.php?content_id=" . rawurlencode($item['id']);
?>