Files
PHP-EM-DB/public/product_join.php
2026-10-29 08:36:44 +09:00

348 lines
10 KiB
PHP

<?php
require_once __DIR__ . '/../database/products/ProductAccess.php';
/**
* JOIN を使った高度な検索(追加課題)
*
* このファイルでは、categoriesテーブルとproductsテーブルを結合して、
* カテゴリ情報と共に商品情報を取得します。
* また、カテゴリ別の商品数を集計する機能も実装します。
*/
// エラー表示設定(開発時のみ)
ini_set('display_errors', 1);
error_reporting(E_ALL);
$message = '';
$messageType = '';
$productsWithCategory = [];
$categoryStats = [];
try {
// ProductAccessを使ってPDO接続を取得
$productAccess = new ProductAccess();
$pdo = $productAccess->getConnection();
// TODO: INNER JOINを使って、商品情報とカテゴリ情報を結合して取得してください
$sql = ""; // ここにJOINを使ったSQL文を記述
// TODO: ここでSQLを実行して結果を$productsWithCategoryに格納
// TODO: カテゴリ別の商品数と平均価格を集計してください
$statsSql = ""; // ここに集計用のSQL文を記述
// TODO: ここでSQLを実行して結果を$categoryStatsに格納
} catch (Exception $e) {
$message = "データの取得に失敗しました: " . $e->getMessage();
$messageType = "error";
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JOIN検索 - 追加課題</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
}
h1 {
color: #333;
margin-bottom: 10px;
font-size: 28px;
}
h2 {
color: #555;
margin-top: 30px;
margin-bottom: 15px;
font-size: 22px;
border-bottom: 2px solid #11998e;
padding-bottom: 10px;
}
.subtitle {
color: #666;
margin-bottom: 25px;
font-size: 14px;
}
.nav-links {
display: flex;
gap: 10px;
margin-bottom: 25px;
flex-wrap: wrap;
}
.nav-links a {
padding: 8px 16px;
background: #11998e;
color: white;
text-decoration: none;
border-radius: 5px;
font-size: 14px;
transition: background 0.3s;
}
.nav-links a:hover {
background: #0e7d72;
}
.message {
padding: 15px;
margin-bottom: 20px;
border-radius: 5px;
font-weight: bold;
}
.message.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th,
td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background: #11998e;
color: white;
font-weight: bold;
}
tr:hover {
background: #f5f5f5;
}
.price {
text-align: right;
font-weight: bold;
color: #e74c3c;
}
.stock {
text-align: center;
}
.category-badge {
display: inline-block;
padding: 4px 12px;
background: #11998e;
color: white;
border-radius: 15px;
font-size: 12px;
font-weight: bold;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin-top: 20px;
}
.stat-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.stat-card h3 {
margin-bottom: 15px;
font-size: 18px;
}
.stat-row {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.stat-row:last-child {
border-bottom: none;
margin-bottom: 0;
}
.stat-label {
font-size: 14px;
opacity: 0.9;
}
.stat-value {
font-size: 16px;
font-weight: bold;
}
.no-data {
text-align: center;
padding: 40px;
color: #999;
font-size: 18px;
}
.info-box {
background: #e7f3ff;
border-left: 4px solid #2196F3;
padding: 15px;
margin-bottom: 20px;
border-radius: 5px;
}
.info-box h3 {
color: #1976D2;
margin-bottom: 10px;
font-size: 16px;
}
.info-box p {
color: #555;
line-height: 1.6;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>🔗 JOIN を使った高度な検索</h1>
<p class="subtitle">Advanced - 複数テーブルを結合してデータを取得します</p>
<div class="nav-links">
<a href="index.php">🏠 トップ</a>
<a href="product_list.php">📋 一覧表示</a>
<a href="product_register.php">📝 新規登録</a>
<a href="product_update.php">✏️ 更新</a>
<a href="product_delete.php">🗑️ 削除</a>
</div>
<div class="info-box">
<h3>📚 追加課題のポイント</h3>
<p>
この課題では、<strong>INNER JOIN</strong> と <strong>LEFT JOIN</strong> を使用します。<br>
・INNER JOIN: カテゴリと商品の両方に存在するデータのみを取得<br>
・LEFT JOIN: 全カテゴリを取得し、商品がない場合も表示<br>
・集計関数: COUNT()で商品数、AVG()で平均価格を計算<br>
・GROUP BY: カテゴリごとにグループ化
</p>
</div>
<?php if ($message): ?>
<div class="message <?php echo $messageType; ?>">
<?php echo htmlspecialchars($message); ?>
</div>
<?php endif; ?>
<!-- カテゴリ別統計情報 -->
<h2>📊 カテゴリ別統計情報</h2>
<?php if (empty($categoryStats)): ?>
<div class="no-data">
統計情報を表示できません。<br>
TODOを実装してください。
</div>
<?php else: ?>
<div class="stats-grid">
<?php foreach ($categoryStats as $stat): ?>
<div class="stat-card">
<h3><?php echo htmlspecialchars($stat['name']); ?></h3>
<div class="stat-row">
<span class="stat-label">商品数:</span>
<span class="stat-value"><?php echo $stat['product_count']; ?> 件</span>
</div>
<div class="stat-row">
<span class="stat-label">平均価格:</span>
<span class="stat-value">
<?php
echo $stat['avg_price']
? '¥' . number_format($stat['avg_price'], 0)
: '商品なし';
?>
</span>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- 商品一覧(カテゴリ情報付き) -->
<h2>📦 商品一覧(カテゴリ情報付き)</h2>
<?php if (empty($productsWithCategory)): ?>
<div class="no-data">
商品情報を表示できません。<br>
TODOを実装してください。
</div>
<?php else: ?>
<table>
<thead>
<tr>
<th>ID</th>
<th>カテゴリ</th>
<th>商品名</th>
<th>カテゴリ説明</th>
<th>価格</th>
<th>在庫</th>
<th>登録日時</th>
</tr>
</thead>
<tbody>
<?php foreach ($productsWithCategory as $product): ?>
<tr>
<td><?php echo htmlspecialchars($product['id']); ?></td>
<td>
<span class="category-badge">
<?php echo htmlspecialchars($product['category_name']); ?>
</span>
</td>
<td><?php echo htmlspecialchars($product['name']); ?></td>
<td><?php echo htmlspecialchars($product['category_description'] ?? '-'); ?></td>
<td class="price">¥<?php echo number_format($product['price']); ?></td>
<td class="stock"><?php echo htmlspecialchars($product['stock']); ?></td>
<td><?php echo htmlspecialchars($product['created_at']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p style="margin-top: 15px; color: #666; font-size: 14px;">
全 <?php echo count($productsWithCategory); ?> 件の商品が表示されています。
</p>
<?php endif; ?>
</div>
</body>
</html>