综合php项目,边路传中成功率对比?

wen PHP项目 3

综合PHP项目:边路传中成功率对比

下面是一个完整的PHP项目示例,用于统计和对比足球比赛中边路传中成功率(可以对比不同球员、不同球队或不同边路)。

综合php项目,边路传中成功率对比?


项目结构

cross_analysis/
├── config/
│   └── database.php
├── src/
│   ├── Database.php
│   └── CrossAnalyzer.php
├── index.php
└── sql/
    └── schema.sql

数据库设计 (schema.sql)

CREATE DATABASE IF NOT EXISTS football_analysis 
DEFAULT CHARACTER SET utf8mb4;
USE football_analysis;
-- 球队表
CREATE TABLE teams (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL
);
-- 球员表
CREATE TABLE players (
    id INT PRIMARY KEY AUTO_INCREMENT,
    team_id INT,
    name VARCHAR(50) NOT NULL,
    position VARCHAR(20),
    FOREIGN KEY (team_id) REFERENCES teams(id)
);
-- 传中记录表(核心)
CREATE TABLE crosses (
    id INT PRIMARY KEY AUTO_INCREMENT,
    match_id INT NOT NULL,
    player_id INT NOT NULL,
    side ENUM('left','right') NOT NULL COMMENT '边路',
    cross_type ENUM('ground','low','high','cutback') NOT NULL COMMENT '传中类型',
    is_success TINYINT(1) NOT NULL COMMENT '是否成功',
    minute INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (player_id) REFERENCES players(id),
    INDEX idx_player (player_id),
    INDEX idx_side (side),
    INDEX idx_match (match_id)
);
-- 示例数据
INSERT INTO teams (name) VALUES ('曼城'), ('利物浦');
INSERT INTO players (team_id, name, position) VALUES
(1, '德布劳内', 'MF'), (1, '福登', 'FW'), (1, 'B席', 'MF'),
(2, '阿诺德', 'DF'), (2, '罗伯逊', 'DF'), (2, '萨拉赫', 'FW');
INSERT INTO crosses (match_id, player_id, side, cross_type, is_success, minute) VALUES
(1, 1, 'right', 'high', 1, 12),
(1, 1, 'right', 'high', 0, 25),
(1, 1, 'right', 'low', 1, 33),
(1, 2, 'left', 'cutback', 1, 45),
(1, 2, 'left', 'ground', 0, 60),
(1, 3, 'right', 'high', 0, 70),
(1, 3, 'right', 'high', 1, 82),
(1, 4, 'right', 'high', 1, 15),
(1, 4, 'right', 'cutback', 0, 40),
(1, 4, 'right', 'high', 1, 55),
(1, 5, 'left', 'low', 1, 20),
(1, 5, 'left', 'low', 0, 50),
(1, 5, 'left', 'high', 0, 77),
(1, 6, 'right', 'ground', 1, 30),
(1, 6, 'right', 'ground', 0, 68);

数据库配置 (config/database.php)

<?php
return [
    'host'     => '127.0.0.1',
    'port'     => 3306,
    'database' => 'football_analysis',
    'username' => 'root',
    'password' => '',
    'charset'  => 'utf8mb4',
];

数据库连接类 (src/Database.php)

<?php
namespace App;
use PDO;
use PDOException;
class Database
{
    private static ?PDO $pdo = null;
    public static function connect(): PDO
    {
        if (self::$pdo === null) {
            $cfg = require __DIR__ . '/../config/database.php';
            $dsn = "mysql:host={$cfg['host']};port={$cfg['port']};dbname={$cfg['database']};charset={$cfg['charset']}";
            try {
                self::$pdo = new PDO($dsn, $cfg['username'], $cfg['password'], [
                    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                ]);
            } catch (PDOException $e) {
                die('数据库连接失败: ' . $e->getMessage());
            }
        }
        return self::$pdo;
    }
}

核心分析类 (src/CrossAnalyzer.php)

<?php
namespace App;
use PDO;
class CrossAnalyzer
{
    private PDO $db;
    public function __construct()
    {
        $this->db = Database::connect();
    }
    /**
     * 按球员统计传中成功率
     */
    public function statsByPlayer(?int $matchId = null): array
    {
        $sql = "SELECT 
                    p.id, p.name AS player_name, t.name AS team_name,
                    COUNT(c.id) AS total,
                    SUM(c.is_success) AS success,
                    ROUND(SUM(c.is_success) / COUNT(c.id) * 100, 2) AS success_rate
                FROM crosses c
                JOIN players p ON c.player_id = p.id
                JOIN teams t ON p.team_id = t.id";
        if ($matchId !== null) {
            $sql .= " WHERE c.match_id = :match_id";
        }
        $sql .= " GROUP BY p.id, p.name, t.name
                  HAVING total > 0
                  ORDER BY success_rate DESC, total DESC";
        $stmt = $this->db->prepare($sql);
        if ($matchId !== null) {
            $stmt->bindValue(':match_id', $matchId, PDO::PARAM_INT);
        }
        $stmt->execute();
        return $stmt->fetchAll();
    }
    /**
     * 按边路(左/右)统计对比
     */
    public function statsBySide(?int $matchId = null): array
    {
        $sql = "SELECT 
                    c.side,
                    COUNT(c.id) AS total,
                    SUM(c.is_success) AS success,
                    ROUND(SUM(c.is_success) / COUNT(c.id) * 100, 2) AS success_rate
                FROM crosses c";
        if ($matchId !== null) {
            $sql .= " WHERE c.match_id = :match_id";
        }
        $sql .= " GROUP BY c.side";
        $stmt = $this->db->prepare($sql);
        if ($matchId !== null) {
            $stmt->bindValue(':match_id', $matchId, PDO::PARAM_INT);
        }
        $stmt->execute();
        return $stmt->fetchAll();
    }
    /**
     * 按球队统计传中成功率
     */
    public function statsByTeam(?int $matchId = null): array
    {
        $sql = "SELECT 
                    t.id, t.name AS team_name,
                    COUNT(c.id) AS total,
                    SUM(c.is_success) AS success,
                    ROUND(SUM(c.is_success) / COUNT(c.id) * 100, 2) AS success_rate
                FROM crosses c
                JOIN players p ON c.player_id = p.id
                JOIN teams t ON p.team_id = t.id";
        if ($matchId !== null) {
            $sql .= " WHERE c.match_id = :match_id";
        }
        $sql .= " GROUP BY t.id, t.name
                  ORDER BY success_rate DESC";
        $stmt = $this->db->prepare($sql);
        if ($matchId !== null) {
            $stmt->bindValue(':match_id', $matchId, PDO::PARAM_INT);
        }
        $stmt->execute();
        return $stmt->fetchAll();
    }
    /**
     * 按传中类型统计
     */
    public function statsByType(?int $matchId = null): array
    {
        $sql = "SELECT 
                    c.cross_type,
                    COUNT(c.id) AS total,
                    SUM(c.is_success) AS success,
                    ROUND(SUM(c.is_success) / COUNT(c.id) * 100, 2) AS success_rate
                FROM crosses c";
        if ($matchId !== null) {
            $sql .= " WHERE c.match_id = :match_id";
        }
        $sql .= " GROUP BY c.cross_type
                  ORDER BY success_rate DESC";
        $stmt = $this->db->prepare($sql);
        if ($matchId !== null) {
            $stmt->bindValue(':match_id', $matchId, PDO::PARAM_INT);
        }
        $stmt->execute();
        return $stmt->fetchAll();
    }
    /**
     * 两两对比:某两名球员或两支球队
     */
    public function comparePlayers(int $playerA, int $playerB): array
    {
        $sql = "SELECT 
                    p.id, p.name,
                    COUNT(c.id) AS total,
                    SUM(c.is_success) AS success,
                    ROUND(SUM(c.is_success) / COUNT(c.id) * 100, 2) AS rate
                FROM players p
                LEFT JOIN crosses c ON p.id = c.player_id
                WHERE p.id IN (:a, :b)
                GROUP BY p.id, p.name";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([':a' => $playerA, ':b' => $playerB]);
        return $stmt->fetchAll();
    }
}

入口文件 (index.php)

<?php
require __DIR__ . '/src/Database.php';
require __DIR__ . '/src/CrossAnalyzer.php';
use App\CrossAnalyzer;
$analyzer = new CrossAnalyzer();
$byPlayer   = $analyzer->statsByPlayer();
$bySide     = $analyzer->statsBySide();
$byTeam     = $analyzer->statsByTeam();
$byType     = $analyzer->statsByType();
function rateColor(float $rate): string {
    if ($rate >= 50) return '#2ecc71';
    if ($rate >= 30) return '#f39c12';
    return '#e74c3c';
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">边路传中成功率对比</title>
<style>
    body { font-family: "Microsoft YaHei", sans-serif; background:#f5f7fa; padding:30px; color:#2c3e50; }
    h1 { text-align:center; }
    h2 { border-left:4px solid #3498db; padding-left:10px; margin-top:40px; }
    table { width:100%; border-collapse:collapse; background:#fff; box-shadow:0 2px 6px rgba(0,0,0,.08); margin-top:10px; }
    th, td { padding:10px 14px; text-align:center; border-bottom:1px solid #eee; }
    th { background:#34495e; color:#fff; }
    tr:hover td { background:#f0f6ff; }
    .bar { height:16px; border-radius:3px; display:inline-block; vertical-align:middle; }
    .rate { font-weight:bold; }
</style>
</head>
<body>
<h1>⚽ 边路传中成功率对比分析</h1>
<h2>📊 1. 球员传中成功率排行</h2>
<table>
    <tr><th>球员</th><th>球队</th><th>传中数</th><th>成功数</th><th>成功率</th><th>可视化</th></tr>
    <?php foreach ($byPlayer as $r): ?>
        <tr>
            <td><?= htmlspecialchars($r['player_name']) ?></td>
            <td><?= htmlspecialchars($r['team_name']) ?></td>
            <td><?= $r['total'] ?></td>
            <td><?= $r['success'] ?></td>
            <td class="rate" style="color:<?= rateColor((float)$r['success_rate']) ?>">
                <?= $r['success_rate'] ?>%
            </td>
            <td>
                <div class="bar" style="width:<?= $r['success_rate'] * 1.5 ?>px;
                     background:<?= rateColor((float)$r['success_rate']) ?>"></div>
            </td>
        </tr>
    <?php endforeach; ?>
</table>
<h2>⚖️ 2. 左路 vs 右路 对比</h2>
<table>
    <tr><th>边路</th><th>传中数</th><th>成功数</th><th>成功率</th></tr>
    <?php foreach ($bySide as $r): ?>
        <tr>
            <td><?= $r['side'] === 'left' ? '左路' : '右路' ?></td>
            <td><?= $r['total'] ?></td>
            <td><?= $r['success'] ?></td>
            <td class="rate" style="color:<?= rateColor((float)$r['success_rate']) ?>">
                <?= $r['success_rate'] ?>%
            </td>
        </tr>
    <?php endforeach; ?>
</table>
<h2>🏆 3. 球队传中成功率对比</h2>
<table>
    <tr><th>球队</th><th>传中数</th><th>成功数</th><th>成功率</th></tr>
    <?php foreach ($byTeam as $r): ?>
        <tr>
            <td><?= htmlspecialchars($r['team_name']) ?></td>
            <td><?= $r['total'] ?></td>
            <td><?= $r['success'] ?></td>
            <td class="rate" style="color:<?= rateColor((float)$r['success_rate']) ?>">
                <?= $r['success_rate'] ?>%
            </td>
        </tr>
    <?php endforeach; ?>
</table>
<h2>🎯 4. 传中类型效果对比</h2>
<table>
    <tr><th>类型</th><th>传中数</th><th>成功数</th><th>成功率</th></tr>
    <?php 
    $typeMap = ['ground'=>'地面传中','low'=>'低平球','high'=>'高空球','cutback'=>'倒三角'];
    foreach ($byType as $r): ?>
        <tr>
            <td><?= $typeMap[$r['cross_type']] ?? $r['cross_type'] ?></td>
            <td><?= $r['total'] ?></td>
            <td><?= $r['success'] ?></td>
            <td class="rate" style="color:<?= rateColor((float)$r['success_rate']) ?>">
                <?= $r['success_rate'] ?>%
            </td>
        </tr>
    <?php endforeach; ?>
</table>
</body>
</html>

运行效果

启动 PHP 内置服务器:

php -S localhost:8000

浏览器访问 http://localhost:8000,即可看到四张对比表:

  1. 球员传中成功率排行 — 谁是最佳传中手
  2. 左路 vs 右路对比 — 哪边进攻更有效
  3. 球队对比 — 哪支球队传中更高效
  4. 传中类型对比 — 地面球、低平球、高空球、倒三角哪种最有效

扩展建议

方向 说明
对手分析 增加 opponent_id,对比"面对不同对手的传中成功率"
时间段分析 minute 分区间(0-15、15-30…)统计体能影响
可视化图表 引入 Chart.js 绘制柱状图/雷达图
API 化 json_encode 输出 RESTful 接口,前端 Vue/React 消费
权限系统 增加登录、只读/编辑角色
数据导入 支持 CSV/JSON 批量导入比赛数据
框架重构 迁移到 Laravel/Symfony,利用 Eloquent ORM

成功率计算公式

成功率 = 成功传中数 / 总传中数 × 100%

"成功"定义建议(可按需求调整):

  • 队友在禁区内接到球
  • 接球后形成射门
  • 直接形成助攻/进球

需要我进一步扩展哪个方向?

  • 🔵 加入 Chart.js 图表可视化
  • 🔵 改成 REST API + 前端页面
  • 🔵 迁移到 Laravel 框架版本
  • 🔵 增加 时间段/对手维度对比

抱歉,评论功能暂时关闭!