Подтвердить что ты не робот

Отображение таблицы в PHP с повторяющимися столбцами

У меня есть следующие данные в базе данных 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, но я несколько раз сталкивался с этой проблемой, используя С#, 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);

Я пропускаю шаги, но надеюсь, вы поняли мою точку зрения. Мне, вероятно, просто нужны ключевые слова, чтобы назвать этот тип проблемы. Каков предпочтительный способ выполнить это?

4b9b3361

Ответ 1

Мне не нравится SQL-решение по одной причине. Вы просите механизм базы данных присоединиться к строке, а затем используете PHP для его разделения. Это похоже на повторную работу. Другая сторона - это то, что если metavalue содержит символ разделителя.

Мой код просто демонстрирует, как вы можете легко группировать данные с 2-мерным массивом в 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);

Нет необходимости временно хранить весь набор результатов в массиве. Вы можете выполнить итерацию прямо по первым данным сортировки по тому полю, в котором вы хотите группировать свои данные и отслеживать состояние.

Однако есть две причины, по которым я предпочитаю этот стиль, и это личное предпочтение (это не обязательное решение):

  • Обычно каждый хочет разделить логику и презентацию. Хранение данных в массиве можно легко передать в механизм шаблонов. Я предпочитаю использовать PHP-шаблон, потому что его легко реализовать, и он работает на скорости света.

  • Я бы предпочел обменять небольшую скорость работы и использование памяти для удобства чтения и обслуживания. Алгоритм короткий и простой для понимания. Если производительность - ваш главный приоритет, вы всегда можете использовать кеш, например, хранить в файле, хранить в memcached или хранить вычисленный результат в базе данных.

Правда, мы группируем данные с разных сторон, но вы предполагаете, что metavalue не содержит разделителя (который в вашем коде равен |). Его можно легко установить, выбрав разделитель, который почти невозможно использовать metavalue. Во всяком случае, ваше решение будет прекрасно работать практически в каждом случае, потому что metavalue, показанный @jvelez, никогда не будет содержать символ |.

Ответ 2

Проблема не в коде php/java/С#, а в конце концов 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, и все связанные с ним метавыводы для этого элемента, вы просто разделите list на '|'.

В PHP вы делаете это с explode(), на С# с String.Split() и, я полагаю, вы сможете найти что-то подобное для любого языка.

Поколение HTML становится довольно тривиальным.


Вот пример PHP (с использованием API соединений PDO):

$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> из вашего примера.

Ответ 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/Name изменяется, у вас есть новая строка таблицы.
  • Каждый раз, когда 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>";

Не так много кода для решения вашей проблемы, потому что сама проблема - всего лишь немного состояния внутри данных.

То, что было описано как события здесь, можно инвертировать, создав свой собственный итератор, который можно передать в систему шаблонов, которая поддерживает итераторы, например. веточка или усы. Полный пример этого показан в предыдущих моих ответах и ​​в блоге:

По сравнению с приведенным выше примером он может выглядеть не так просто, однако, надеюсь, он дает достаточно кода, чтобы вы решили, куда идете. Он довольно расширяемый.

Ответ 4

Вот решение, оно похоже на @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>

Это, кажется, дает желаемые результаты. Надеюсь, что это поможет.

Ответ 5

Вы спросили, как назвать этот тип проблемы: его названная группировка. Одна из возможностей заключается в том, чтобы разрешить ее в SQL с помощью предложения GROUP BY (но разные механизмы SQL имеют различную поддержку для них, особенно когда речь идет о более сложных агрегациях сгруппированных данных).

В С# базовое решение является однострочным с помощью 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);
}

Вышеприведенный фрагмент концептуально совпадает с ответами на основе PHP от drew010 и invisal (они используют двумерный PHP-массив, который концептуально такой же, как и выше Dictionary of Lists). Я попытался сделать шаги максимально ясными и не использовать слишком много специальных функций С# (например, в реальной программе вы должны использовать собственный класс для сложных ключей вместо простого Tuple), поэтому выше фрагмент должен быть легко переносимым на Java и другие языки.

Как вы можете видеть по всем приведенным здесь примерам, после группировки данных генерация HTML всегда одна и та же (два вложенных цикла).

Ответ 6

Я бы решил это по-другому. Прямо сейчас у вас проблемы, потому что схема базы данных не нормализована. Вместо этого я начну с изменения схемы. Сейчас у вас есть это:

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 (чего нет, мета просто неправильно названа из-за Именования 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>";

Теперь, со всем сказанным, я бы лично реализовал его немного по-другому, используя для этого первоклассные бизнес-объекты и карты данных.

И я сделал несколько фелоний здесь, но я сделал это для простоты. Вот что я бы сделал по-другому:

  • Не выполнять логику в конструкторе объекта. Вместо этого я бы ввел его или использовал factory для предварительного заполнения данных.
  • Отметить концепцию отношений в отдельных бизнес-объектах.
  • Отделите логику выталкивания от логики отображения.
  • Назовите вещи лучше (metavalue не является хорошим именем для поля).

Но это действительно зависит от точных требований приложения. Нет ни одного (или двух) правильных способов сделать это. У каждого из возможных способов есть плюсы и минусы. Научитесь взвешивать компромиссы, чтобы вы могли выбрать правильный для своих требований...

Ответ 7

У меня была подобная таблица один раз, и я закончил использование некоторого SQL для возврата данных в виде отдельных строк. Я изменил свои объекты, чтобы использовать ваш sql, и написал простую функцию, которая выводит ваши данные в таблицу, следуя формату, который вы использовали в своем вопросе.

Основная агрегирующая функция запроса, которую я использовал, - это MySQL GROUP_CONCAT. Это похоже на функцию implode(). Разделитель по умолчанию - это запятая, поэтому, если у вас есть запятые в ваших метаданных, вы можете изменить их на другой маркер, добавив SEPARATOR 'yourSeparator' в скобки после имени столбца. Вы также можете использовать distinct infront имени столбца, чтобы выделять только отдельные строки в 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 будет пропускать пустые поля, поэтому вам нужно будет привести их хитро, иначе ваши идентификаторы autonum не будут соответствовать вашим результатам. Я сделал это здесь с простой coalesce() внутри функции group_concat.

Чтобы разместить эти дополнительные биты, я немного переписал объект. Это должно сделать все, что вам нужно, я оставил свои отладочные заметки, в которых задана личная переменная isDebug, которую я установил в значение false, но установка ее в 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

Ответ 8

Я рекомендую создать функцию в базе данных

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

и используйте чистую инструкцию на стороне PHP, как это.

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

то вам нужно сделать

echo $row["meta_output"]; 

как и любой другой столбец. Это то, что я буду использовать.

Ответ 9

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

?>

Ответ 10

Вот мой второй подход к проблеме, как с первым,, я все еще поклонник решения этого вопроса с выходом, например:

<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];
    }
}

Полная демонстрация в кодедепе - Должен признать, что это содержит немного итераторной черной магии.

Ответ 11

Это довольно близко к некоторым другим ответам, но я думаю, что немного чище и легче следовать.

Я создаю трехмерный массив до любого выхода для удобства использования. Вы можете сделать все сразу, но я считаю, что более чистый код, как правило, отделяет его (чтение из базы данных отличается от вывода, например, так что держите их отдельно). Таким образом, у вас также есть удобный массив, если вам когда-либо понадобится обратиться к человеку 33 15-й метавыход $resultArr[33]["meta"][15].

Я также добавил имена классов и идентификатор для каждой строки результатов для упрощения стилизации 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;
?>

Ответ 12

Здесь я не группирую на 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> 

Ответ 13

Вот код, который вы хотите. Чтобы избежать конфликта с вашим кодом, я не использую никакой концепции объекта класса, поэтому код написан простым способом. Это выглядит просто, но он не вызывает сомнений. Обратите внимание, что код размещается старшим программистом, работающим на 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>

Ответ 14

<?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>";