12

我在 MySQL 数据库中有以下数据:

Autonum  ID  Name  MetaValue
1         1  Rose  Drinker
2         1  Rose  Nice Person
3         1  Rose  Runner
4         2  Gary  Player
5         2  Gary  Funny

我现在在 PHP 中工作,但我在使用 C#、Java 和其他语言时遇到过几次这个问题。

现在我过去和现在的目标是以以下格式显示数据:

<table>
    <thead>
    <th>Name</th>
    <th>MetaValue1</th>
    </thead>
    <tbody>
           <tr>      
               <td>Rose</td>
                <td>
                    <ul>
                        <li>Drinker</li> 
                        <li>Nice Person</li>
                        <li>Runner</li>
                     </ul>
                </td>
            </tr>
            <tr>
                <td>Gary</td>
                <td>
                    <ul>
                         <li>Player</li>
                         <li>Funny</li>
                 </td>
                </tr>
        </tbody>

</table> 

我之前通过创建一个代表我的 SQL 表的类来解决这个问题。然后我创建了一个Dictionary要举行EmployeeId的班级。

Dictionary<string,MyTable> MyData = new <string,MyTable>();
Table MyMetaData = new Table();
MyMetaData SomeMetaData=getMetaValueList();//imagine a web service that does that
MyData.add(EmployeeId,SomeMetaData);

我跳过了步骤,但我希望你明白我的意思。我可能只需要所谓的此类问题的关键字。实现此目的的首选方法是什么?

4

14 回答 14

11

出于一个原因,我不喜欢 SQL 解决方案。您要求数据库引擎加入字符串,然后使用 PHP 将其拆分。这就像重复的工作。另一个缺点是 ifmetavalue包含分隔符。

我的代码只是演示了如何在 PHP 中使用二维数组轻松地对数据进行分组,并且由编码人员决定从遗留 mysql_* 或 PDO 数据库中检索数据的方法。

// get result
$result = mysql_query("SELECT autonum,id,name,metavalue FROM table");

// loop through each row
while ($row = mysql_fetch_assoc($result)) {
    $table[$row['name']][] = $row['metavalue'];
}

// print it out
print_r($table);

无需将整个结果集临时存储到数组中。您可以通过首先按要对数据分组并跟踪状态的字段对数据进行排序来直接迭代。

但是,我喜欢这种风格有两个原因,这是个人喜好(这不是必须做的解决方案):

  1. 通常,每个人都希望将逻辑和表示分开。在数组中存储数据可以很容易地传递给您的模板引擎。我更喜欢使用 PHP 模板引擎,因为它易于实现并且运行速度极快。

  2. 我宁愿用轻微的性能速度和内存使用来换取可读性和可维护性。该算法简短易懂。如果性能是您的首要任务,您始终可以使用缓存,例如存储在文件中、存储在 memcached 中或将计算结果存储在数据库中。

没错,我们正在对来自不同方面的数据进行分组,但您假设metavalue不包含分隔符(在您的代码中是 |)。通过选择几乎不可能使用的分隔符可以很容易地修复它metavalue。无论如何,您的解决方案几乎在所有情况下都可以正常工作,因为metavalue@jvelez 显示的永远不会包含字符 |。

于 2012-07-17T20:44:24.680 回答
9

问题不在于 php/java/c# 代码,而在于 SQL 端。

SELECT ID      AS id,
       Autonum AS num,
       Name    AS name,
       GROUP_CONCAT
       (
           ORDER BY MetaValue DESC SEPARATOR '|'
       )       AS list
FROM Things
GROUP BY Things.ID

通过这种方式,您可以获得一个name值列表,以及该项目的所有关联 MetaValue,您只需拆分liston '|'

在 PHP 中,您使用explode().,在 C# 中String.Split(),我假设您将能够找到任何语言的类似内容。

HTML 的生成变得非常简单。


这是一个 PHP 示例(使用了 PDO 连接 API):

$statement = $pdo->query( $sql );

if ( $statement->execute() )
{
    $data = $statement->fetchAll(PDO::FETCH_ASSOC);

    foeach ( $data as $row )
    {
         echo '<tr><td>' , $row['name'] , '</td>';
         echo '<td><ul><li>' , str_replace( '|', '</li><li>' , $row['list']) ;
         echo '</li></ul></td></tr>';
    }
}

<tbody>将从您的示例中生成内容。

于 2012-07-17T18:36:03.643 回答
3

请查看您的数据,它是排序和排序的:

Autonum  ID  Name  MetaValue
1         1  Rose  Drinker
2         1  Rose  Nice Person
3         1  Rose  Runner
4         2  Gary  Player
5         2  Gary  Funny

所以你可以做的是通过输出来解决这个问题:

  • 每次 ID/名称更改时,您都会有一个新的表格行。
  • 每次 MetaValue 更改时,您都会有一个新的列表条目。

您需要做的就是提前查看下一个元素以查看更改。这意味着您提前进行迭代(预先计算的迭代)或进行缓存迭代(例如CachingIterator在 PHP 中使用)。

如果您随后也跟踪状态,您可以根据每个条目说明是否:

  • 需要在它之前打开一个表行。
  • 之后需要关闭一个表行。

实际上它更简单,这里是一个基于数组的行迭代示例(Demo):

$rows = array(
    array(1, 1, 'Rose', 'Drinker'),
    array(2, 1, 'Rose', 'Nice Person'),
    array(3, 1, 'Rose', 'Runner'),
    array(4, 2, 'Gary', 'Player'),
    array(5, 2, 'Gary', 'Funny'),
);

echo "<table>\n    <thead>\n    <th>Name</th>\n    <th>MetaValue1</th>\n    </thead>\n    <tbody>\n";

$events = new Events();
$last = array(2 => null);
foreach($rows as $row)
{
    if ($last[2] !== $row[2]) {
        $events->newRow($row[2]);
    }
    $events->newItem($row[3]);
    $last = $row;
}
$events->closeRow();

echo "    </tbody>\n</table>";

没有多少代码可以解决您的问题,因为问题本身只是数据内部的一点点状态。

可以通过创建您自己的迭代器来反转这里描述的事件,该迭代器可以传递给支持迭代器的模板系统,例如 twig 或 mustache。我以前的答案和博客文章中显示了一个完整的例子:

与上面的示例相比,它可能看起来并不那么简单,但是它希望提供足够的代码来让您下定决心要去哪里。它是相当可扩展的。

于 2012-07-23T16:48:55.400 回答
2

我会以不同的方式解决这个问题。现在,您遇到了问题,因为您的数据库模式未标准化。相反,我将从更改架构开始。现在,你有这个:

CREATE TABLE foo {
    `autonum` INT(12) NOT NULL AUTO_INCREMENT,
    `id` INT(12) NOT NULL,
    `name` VARCHAR(255) NOT NULL,
    `metaValue` VARCHAR(255) NOT NULL,
    PRIMARY KEY(autonum)
)

相反,我会将其拆分为两个表:

CREATE TABLE user {
    `id` INT(12) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(255) NOT NULL,
    PRIMARY KEY(id)
)
CREATE TABLE user_meta {
    `user_id` INT(12) NOT NULL,
    `value` VARCHAR(255) NOT NULL
)

忽略这看起来像一个 EAV 表的事实(它不是,meta 只是由于 OP 的命名而被错误命名),应该清楚这如何更容易。所以让我们看看你的记录,并建立表格:

#User
id   | name
1    | Rose
2    | Gary

#User Meta
user_id | value
1       | drinker
1       | Nice Person
1       | Runner
2       | Player
2       | Funny

现在我们有了数据,让我们看看如何提取它。假设我们总是想打印出所有用户(限制它很简单,但为了清楚起见,我们现在可以全部完成)。所以,我会在这里为每个表设置一个简单的类,将数据缓存到一个查询中。

class User implements IteratorAggregate {
    protected $data = array();
    public function __construct() {
        $query = mysql_query("SELECT id, name FROM user");
        while ($row = mysql_fetch_assoc($query)) {
            $this->data[$row['id']] => $row['name'];
        }
    }
    public function getIterator() {
        return new ArrayObject($this->data);
    }
}

class User_Meta {
    protected $data = array();
    public function __construct() {
        $query = mysql_query("SELECT user_id, value FROM user_meta");
        while ($row = mysql_fetch_assoc($query)) {
            if (empty($this->data[$row['user_id']])) {
                $this->data[$row['user_id']] = array();
            }
            $this->data[$row['user_id']][] = $row['value'];
        }
    }
    public function getDataForUserId($id) {
        return empty($this->data[$id]) ? array() : $this->data[$id];
    }
}

现在,构建输出变得微不足道:

$users = new User;
$meta = new User_Meta;

echo "<table>
<thead>
<th>Name</th>
<th>MetaValue1</th>
</thead>
<tbody>";

foreach ($users as $id => $name) {

    echo "<tr>      
           <td>".htmlspecialchars($name)."</td>
            <td>
                <ul>";
    foreach ($meta->getDataForUserId($id) as $metaValue) {
         echo "<li>" . htmlspecialchars($metaValue) . "</li>";
    }

    echo "      </ul>
            </td>
           </tr>";
}

echo "</tbody></table>";

现在,说了这么多,我个人将实现它有点不同,使用一流的业务对象和数据映射器来处理所有事情。

我确实在这里犯了一些重罪,但我这样做是为了简单。这是我会做的不同的事情:

  1. 不在对象的构造函数中做逻辑。相反,我会注入它或使用工厂来预填充数据。
  2. 将关系的概念抽象为单独的业务对象。
  3. 从显示逻辑中分离出拉取逻辑。
  4. 更好地命名事物(元值不是字段的好名称)。

但这实际上取决于应用程序的确切要求。没有一种(或两种)正确的方法可以做到这一点。每种可能的方法都有优点和缺点。学会权衡权衡,以便您可以根据自己的要求选择合适的...

于 2012-07-23T18:14:44.733 回答
2

您问如何称呼这种类型的问题:它称为分组。一种可能性确实是在 SQLGROUP BY子句的帮助下解决它(但不同的 SQL 引擎对这些有不同的支持,特别是当涉及到要分组的更复杂的数据聚合时)。

在 C# 中,由于 LINQ,基本的解决方案是单行的,只需使用GroupBy扩展方法:

var data = new [] {
    new { RowId = 1, EmpId = 1, Name = "Rose", Value = "Drinker" },
    new { RowId = 2, EmpId = 1, Name = "Rose", Value = "Runner" },
    new { RowId = 3, EmpId = 2, Name = "Gary", Value = "Player" },
};

var grouping = data.GroupBy(row => row.Name);
var sb = new StringBuilder();
foreach (var grp in grouping) {
    sb.AppendFormat("<tr><td>{0}</td>\n", grp.Key);
    sb.AppendLine("  <td><ul>");
    foreach (var element in grp) {
        sb.AppendFormat("    <li>{0}</li>\n", element.Value);
    }
    sb.AppendLine("  </ul></td>\n</tr>");
}

Console.WriteLine(sb.ToString());

背后的基本思想GroupBy只是某种关联数组或字典,将分组的属性作为键以及与该键关联的所有行的列表。例如(使用具有上述定义的变量数据):

var map = new Dictionary<Tuple<int, string>, List<object>>();
foreach (var row in data) {
    var key = Tuple.Create(row.EmpId, row.Name);
    if (!map.ContainsKey(key))
        map.Add(key, new List<object>());

    map[key].Add(row);
}

上面的代码片段在概念上与来自 d​​raw010 和 invisal 的基于 PHP 的答案相同(它们使用二维 PHP 数组,在概念上与上面的列表字典相同)。我试图使步骤尽可能明确,并且不使用 C# 的太多特殊功能(例如,在实际程序中,您应该倾向于使用它自己的适当类来处理复杂的键而不是简单的元组),所以上面片段应该很容易移植到 Java 和其他语言。

正如您在此处的所有示例中所看到的,在对数据进行分组之后,HTML 生成始终是相同的(两个嵌套循环)。

于 2012-07-22T16:35:02.377 回答
2

这是一个解决方案,它类似于@invisal 的方式,我们基于表的属性创建数组以将结果分组在一起。

<?php

$mysqli = new mysqli("localhost", "uname", "pword", "wordpress");
if ($mysqli->connect_errno) {
    die("Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error);
}

$res = $mysqli->query("SELECT * FROM wp_postmeta");

// array that will hold the final results
$results = array();

// while we have more rows
while ($row = $res->fetch_assoc()) {
    // check to see if we already have this ID
    if (!array_key_exists($row['ID'], $results)) {
            // if not create a new index for it in $results, which holds an array
        $results[$row['ID']] = array();
    }

    // create new stdClass object that holds the row data
    $obj = new stdClass;
    $obj->Name = $row['Name'];
    $obj->MetaValue = $row['MetaValue'];

    // append this data to our results array based on the ID
    $results[$row['ID']][] = $obj;
}
?>

<table>
    <thead>
    <th>Name</th>
    <th>MetaValue</th>
    </thead>
    <tbody>

<?php foreach($results as $id => $values): ?>
    <tr>
        <td style="vertical-align: top"><?php echo $values[0]->Name ?></td>
        <td>
            <ul>
            <?php foreach($values as $value): ?>
                <li><?php echo $value->MetaValue ?></li>
            <?php endforeach; ?>
            </ul>
        </td>
    </tr>
<?php endforeach; ?>
    </tbody>
</table>

这似乎会产生您想要的结果。希望有帮助。

于 2012-07-21T19:31:30.637 回答
1

我曾经有一个类似的表,最后我使用一些 SQL 将数据作为单独的行返回。我已经修改了我的对象以使用您的 sql 并编写了一个简单的函数,它将按照您在问题中使用的格式在表中输出您的数据。

我使用的主要查询聚合函数是 MySQL GROUP_CONCAT。这类似于一个implode()函数。默认分隔符是逗号,因此如果您的元数据中有逗号,您可以通过SEPARATOR 'yourSeparator'在列名称后面的括号内添加来将其更改为不同的标记。您还可以使用distinct列名的前面来仅选择 group_concat 中的不同行。

我已将表头部分放在try语句中,这样如果您没有任何结果,您将不会显示半生成的表。

<?php


class myUser
{
    public $ID;
    public $name;
    public $metas;
}

class myTables
{
    public $SQL="
select 
    ID,
    name,
    GROUP_CONCAT(metavalue) as metas
FROM 
    table1
GROUP BY 
    ID,
    name;";

    public function outputTable()
    {
        $hostname="mysql:host=localhost;dbname=test";
        $username="testdb";
        $password="testdbpassword";

        try{
            echo "
        <table>
            <thead>
            <th>Name</th>
            <th>MetaValue</th>
            </thead>
            <tbody>
            ";
            $dbh = new PDO($hostname, $username, $password);
            $stmt = $dbh->query($this->SQL);
            $obj = $stmt->setFetchMode(PDO::FETCH_INTO, new myUser);
            foreach($stmt as $myUser)
            {
                echo "
                        <tr><td>".$myUser->name."</td><td><ul>";
                $metas=explode(",", $myUser->metas);
                for($i=0;$i<count($metas);$i++)
                {
                    echo "<li>".$metas[$i]."</li>";
                }
                echo "</ul></td></tr>";
            }
            unset($obj);
            unset($stmt);
            unset($dbh);
            echo "
            </tbody>
        </table>
            ";
        }
        catch(PDOException $e){
            echo 'Error : '.$e->getMessage();
            exit();
        }
    }

}

$myPage= new myTables();
$myPage->outputTable();
?>

示例输出:

<table>
    <thead>
    <th>Name</th>
    <th>MetaValue1</th>
    </thead>
    <tbody>
    <tr><td>Rose</td><td><ul><li>Drinker</li><li>Nice Person</li><li>Runner</li></ul></td></tr>
    <tr><td>Gary</td><td><ul><li>Player</li><li>Funny</li></ul></td></tr>
   </tbody>
</table>

我从您的查询中删除了Autonum,否则它会破坏聚合函数。但是,如果您确实需要它,则必须以类似的方式对其进行聚合。该group_concat函数将跳过空字段,因此您需要巧妙地将它们带入,否则您的自动编号 ID 将与您的结果不匹配。我在这里用一个简单coalesce()的内部group_concat函数做到了这一点。

为了容纳这些额外的部分,我稍微重写了对象。这应该可以满足您的所有需求,我已经留下了由我设置为 false 的私有变量设置的调试说明isDebug,但是将其设置为 true 会在对象运行函数时为您提供额外的信息。这将帮助您进行调试,因为我假设您的源数据实际上要复杂得多。

<?php


class myUser
{
    public $ID;
    public $name;
    public $metaDesc;
    public $metaNum;
    public $metaDescs;
    public $metaNums;
}
// Basic User stored here. One Object per Row is created.

class myTables
{
    private $isDebug=false; // Change this to true to get all validation messages;
    private $hostname="mysql:host=localhost;dbname=test";
    private $username="testdb";
    private $password="testdbpassword";
    private $myUsers=array();
    private $curUser = myUser;
    private $userCount=0;
    private $SQL="
select 
    ID,
    name,
    GROUP_CONCAT(coalesce(metavalue,'null')) as metaDesc,
    group_concat(autonum) as metaNum
FROM 
    table1
GROUP BY 
    ID,
    name;";

    public function getuserData()
    {
        $dbh = new PDO($this->hostname, $this->username, $this->password);
        $stmt = $dbh->query($this->SQL);
        $obj = $stmt->setFetchMode(PDO::FETCH_INTO, new myUser);
        $userCount=0;
        foreach($stmt as $myUser)
        {
            $this->myUsers[$userCount]=new myUser;
            $this->myUsers[$userCount]->ID=$myUser->ID;
            $this->myUsers[$userCount]->name=$myUser->name;
            $this->myUsers[$userCount]->metaDesc=$myUser->metaDesc;
            $this->myUsers[$userCount]->metaNum=$myUser->metaNum;
            $userCount++;
        }
        $this->userCount=$userCount;
        if($this->isDebug){echo "There are ".$this->userCount." users found.<br>";}
        unset($obj);
        unset($stmt);
        unset($dbh);
    }
    // Pulls the data from the database and populates the this->object.

    public function outputTable()
    {
        echo "
    <table>
        <thead>
        <th>Name</th>
        <th>MetaValue</th>
        </thead>
        <tbody>
        ";
        for($i=0; $i<$this->userCount; $i++)
        {
            if($this->isDebug){echo "Running main cycle. There are ".$this->userCount." elements.";}
            $this->myUsers[$i]->metaDescs=explode(',', $this->myUsers[$i]->metaDesc);
            $this->myUsers[$i]->metaNums=explode(',', $this->myUsers[$i]->metaNum);
            if($this->isDebug){echo "This user has ".(count($this->myUsers[$i]->metaDescs))." segments<br>";}
            if($this->isDebug){echo "My first segment is ".($this->myUsers[$i]->metaDesc)."<br>";}
            echo "<tr><td>".$this->myUsers[$i]->name."</td><td><ul>";
            for($j=0;$j<count($this->myUsers[$i]->metaDescs);$j++)
            {
                echo "<li>ID: ".$this->myUsers[$i]->metaNums[$j]." - ".$this->myUsers[$i]->metaDescs[$j]."</li>";
            }
            echo "</ul></td></tr>";
        }
            echo "
            </tbody>
        </table>
            ";
    }
    // Outputs the data held in the object into the table as required.

}

$myPage= new myTables();
$myPage->getUserData();
$myPage->outputTable();
?>

输出现在如下所示:

<table>
    <thead>
    <th>Name</th>
    <th>MetaValue</th>
    </thead>
    <tbody>
    <tr><td>Rose</td><td><ul><li>ID: 1 - Drinker</li><li>ID: 2 - Nice Person</li><li>ID: 3 - Runner</li></ul></td></tr>
    <tr><td>Gary</td><td><ul><li>ID: 4 - Player</li><li>ID: 5 - Funny</li><li>ID: 6 - null</li><li>ID: 7 - Smelly</li></ul></td></tr>
</tbody>
</table>

Name    MetaValue
Rose    

ID: 1 - Drinker
ID: 2 - Nice Person
ID: 3 - Runner

Gary    

ID: 4 - Player
ID: 5 - Funny
ID: 6 - null
ID: 7 - Smelly
于 2012-07-22T05:50:30.473 回答
1

我建议你在数据库中创建一个函数

create function get_meta(@autonum int)
 returns varchar(500)
as
 declare @meta varchar(500)='<ul>';
 select @meta=@meta+'<li>'+MetaValue+'</li>' from mytable where Autonum=@autonum
 return @meta+'</ul>';

并像这样在 PHP 端使用一个干净的语句。

select id,autonum,name, get_meta(autonum) as meta_output from mytable

那么你需要做

echo $row["meta_output"]; 

像任何其他专栏一样。这就是我会使用的。

于 2012-07-23T12:13:46.943 回答
1

我这里既没有在MySql 端也没有在PHP 端进行分组,即没有使用php 的内存进行分组,也没有使用mysql 函数。只是控制打印。

您可以在此处查看以下代码的工作示例

<?php
$result = mysql_query("SELECT name,metavalue FROM table");

$count = mysql_num_rows($result);
?>
     <table>
        <thead>
        <th>Name</th>
        <th>MetaValue1</th>
        </thead>
        <tbody>
    <?php
$i     = 0;
// loop through each row
$start = 1;

while ($row = mysql_fetch_assoc($result) {
    if ($name != $row['name']) {// row with new name
        if ($start) {
            $start = 0;
            echo "<tr>";
            echo "<td>" . $row['name'] . "</td>
                 <td><ul><li>" . $row['metadata'] . "</li>";
        } else {
            $start = 1;
            echo "</ul></td>";
            echo "</tr><tr>";
            echo "<td>" . $row['name'] . "</td>
                 <td><ul><li>" . $row['metadata'] . "</li>";
        }
    } else {//row with the same name
        echo "<li>" . $row['metadata'] . "</li>";
        if ($i == $count - 1) {
            echo "</ul></td></tr>";
        }
    }
?>


<?php
    $name = $row['name'];
    $i++;
}
?>
    </tbody>
    </table> 
于 2012-07-26T10:44:51.263 回答
1
This would print exactly what you need within the <tbody> tags :

<?php

$result = mysql_query("SELECT name, metavalue FROM table");
$previous_name = ""; //Variable to keep track of previous name value in array()

print '<table><thead><th>Name</th><th>Metavalue</th></thead><tbody>'; //print the static table header

while ($row = mysql_fetch_array($result)) {
    $name = $row['name'];
    $meta_value = $row['metavalue'];

    if (($previous_name != $name) && ($previous_name !="")) {
        //If $name has changed, close tags, reset $previous_name
        print '</ul></td></tr>';
        $previous_name = "";
    }

    if ($previous_name == "") {
        //New value of $name, open tags, set $name to $previous_name
        print '<tr><td>'.$name.'</td><td><ul>';
        $previous_name = $name;
    } 

    if ($previous_name == $name) {
        //print contents of $meta_value if $name is the same
        print '<li>'.$meta_value.'</li>';
    }   
}
//Once there are no more values in the array, close the static table header
print '</tbody></table>'; //close the static table header

?>
于 2012-07-23T13:35:49.450 回答
1

这与其他一些响应非常接近,但我认为更清晰,更容易理解。

为了便于使用,我在任何输出之前创建了一个 3 维数组。您可以一次完成所有操作,但我发现看起来更清晰的代码往往会将其分开(例如,从数据库读取与输出不同,因此请将它们分开)。这样,如果您需要引用 person 33 的第 15 个元值,您也有一个方便的数组$resultArr[33]["meta"][15]

我还为每个结果行添加了类名和一个 ID,以便于 CSS 样式。

<?php
// DB connect //

// Get the DB Data
$result = mysql_query("SELECT Autonum, ID, Name, MetaValue FROM table GROUP BY ID");

// Set Start Values
$resultArr = array();
$curRow = -1;
$curMeta = -1;

// Loop Through the Rows
while ($row = mysql_fetch_assoc($result)) {
    // Start of a new ID, Set the name and Create the MetaValue Array
    if ($curRow != $row["ID"]){
        $curRow = $row["ID"];
        $resultArr[$curRow]["name"] = $row["Name"];
        $resultArr[$curRow]["meta"] = array();
        $curMeta = 0;
    }
    // Add the MetaValue
    $resultArr[$curRow]["meta"][$curMeta] = $row["MetaValue"};
    $curMeta++;
}
/* Array looks like:
$resultArr[1]["name"] = "Rose";
$resultArr[1]["meta"][0] = "Drinker";
$resultArr[1]["meta"][1] = "Nice Person";
$resultArr[1]["meta"][2] = "Runner";
$resultArr[2]["name"] = "Gary";
$resultArr[2]["meta"][0] = "Player";
$resultArr[2]["meta"][1] = "Funny";
*/

// Start the Table
$out = "<table>";
$out .= "\n<thead>";
$out .= "\n<th>Name</th>";
$out .= "\n<th>MetaValue1</th>";
$out .= "\n</thead>";
$out .= "\n<tbody>";

// Add the Rows
foreach($resultArr as $id => $col){
    $out .= "\n\n<tr id='result_" . $id . "'>";
    $out .= "\n<td class='rowName'>" . $col["name"] . "</td>";
    $out .= "\n<td class='rowMeta'>";
    $out .= "\n<ul>";
    foreach($col["meta"] as $meta){
        $out .= "\n<li>".$meta."</li>";
    }
    $out .= "\n</ul>";
    $out .= "\n</td>";
    $out .= "\n</tr>";
}

// Close the Table
$out .= "\n</tbody>";
$out .= "\n</table>";

// Print It Out Where You Need It
echo $out;
?>
于 2012-07-24T19:30:19.233 回答
1

这是我在这个问题上的第二种方法,就像第一种方法一样,我仍然喜欢用输出解决这个问题,例如:

<table>
    <?php foreach($users as $name => $columns) : ?>
        <tr>
            <td>
                <?php echo htmlspecialchars($name); ?>
            </td>
            <td>
                <ul>
                    <?php foreach($columns as $column) : ?>
                        <li><?php echo htmlspecialchars($column); ?></li>
                    <?php endforeach ?>
                </ul>
            </td>
        </tr>
    <?php endforeach ?>
</table>

这里使用数组结构模拟数据库结果集,但使用典型的迭代器:

$rows = array(
    array(1, 1, 'Rose', 'Drinker'),
    array(2, 1, 'Rose', 'Nice Person'),
    array(3, 1, 'Rose', 'Runner'),
    array(4, 2, 'Gary', 'Player'),
    array(5, 2, 'Gary', 'Funny'),
);
$result = new ArrayIterator($rows);

$users = new Users($result);

这表明这是一个单一的查询,没有子选择,不需要重新分组(如第一个问题中所述)根据它的顺序执行此操作。

Users仅缺少该类的代码:

class Users extends IteratorIterator
{
    private $last;
    public function __construct($it) {
        parent::__construct(new NoRewindIterator($it));
    }
    public function current() {
        $current = $this->getInnerIterator()->current();
        $this->last = $current[2];
        return new Columns($this->getInnerIterator(), $this->last);
    }
    public function key() {
        return $this->last;
    }
    public function next() {
        $current = $this->getInnerIterator()->current();
        if ($this->last === $current[2] || $current === NULL) {
            parent::next();
        }
    }
}

class Columns extends IteratorIterator
{
    private $name;
    public function __construct($it, $name) {
        $this->name = $name;
        parent::__construct($it);
    }
    public function valid()
    {
        $current = parent::current();
        return parent::valid() && $current[2] === $this->name;
    }
    public function current() {
        $current = parent::current();
        return $current[3];
    }
}

键盘上的完整演示- 我必须承认这包含一些迭代器黑魔法。

于 2012-07-23T19:36:08.890 回答
0

这是您想要的代码。为了避免与您的代码发生任何冲突,我没有使用任何类对象概念,因此代码是以简单的方式编写的。它看起来很简单,但毫无疑问它正在工作。请注意,代码是由过去一年半从事 php 工作的高级程序员放置的。所以不要介意代码的简单时尚


    <table>
        <thead>
        <th>Name</th>
        <th>MetaValue1</th>
        </thead>
        <tbody>
        <?php
        mysql_select_db("dbName", $con);
        $result = mysql_query("SELECT DISTINCT Name,ID FROM tableName");
        while($row = mysql_fetch_array($result))
        {
            $userName = $row['Name'];
            echo $userId = $row['ID'];

        ?>     
             <tr>      
                   <td><?=$userName?> <?=$userId?></td>
                    <td>
                       <ul>
                        <?php
                        $resultNew = mysql_query("SELECT * FROM tableName WHERE ID =$userId");
                        while($row = mysql_fetch_array($resultNew))
                        {
                            $mValue = $row['MetaValue'];
                        ?>
                        <li><?=$mValue?></li> 
                        <?php   
                        }
                        ?>
                        </ul>
                    </td>
                </tr>
          <?php } ?>      
            </tbody>

    </table>
于 2012-07-23T11:32:26.790 回答
0
<?php
/**
 * Displaying a table in PHP with repeated columns
 * @link http://stackoverflow.com/a/11616884/367456
 */

class Events
{
    private $row = 0;

    public function newRow($name)
    {
        $this->closeRow();
        echo "        <tr><td>$name</td><ul>";
        $this->row++;
    }

    public function closeRow()
    {
        if (!$this->row) return;
        echo "</ul></tr>\n";
        $this->row = 0;
    }

    public function newItem($name)
    {
        echo "<li>$name</li>";
    }
}

$rows = array(
    array(1, 1, 'Rose', 'Drinker'),
    array(2, 1, 'Rose', 'Nice Person'),
    array(3, 1, 'Rose', 'Runner'),
    array(4, 2, 'Gary', 'Player'),
    array(5, 2, 'Gary', 'Funny'),
);

echo "<table>\n    <thead>\n    <th>Name</th>\n    <th>MetaValue1</th>\n    </thead>\n    <tbody>\n";

$events = new Events();
$last = null;
foreach ($rows as $row) {
    if (@$last[2] !== $row[2]) {
        $events->newRow($row[2]);
    }
    $events->newItem($row[3]);
    $last = $row;
}
$events->closeRow();

echo "    </tbody>\n</table>";
于 2012-07-26T03:55:10.057 回答