Как результат комбинирует результаты двух таблиц MySQL с использованием PDO

У меня есть следующие две структуры таблиц в MySQL, которые записывают детали конференц-вызова и тех участников, которые присоединились к нему:

Таблица: конференция:

conference_sid, date_created, date_completed, RecordURL, PIN 

* date_created и * date_completed – временные метки

Таблица: участники:

 conference_sid, call_sid, call_from, name_recording 

Я хочу вывести простую таблицу, которая отображает следующие результаты для каждой конференции в качестве отдельной строки:

 <table> <thead> <th>Date</th> <th>Duration</th> <th>Participants</th> <th>Recording</th> </thead> <tbody> <tr id="conference_sid"> <td>date_created</td> <td>duration: [date_completed - date_created in h/mm/ss]</td> <td> <li><a href="name_recording">call_from</a> [for all participants in that conference_sid] <li>call_from... </td> <td> <a href="RecordURL">Call recording</a> </td> </tr> <tr id="conference_sid"> ... </tr> </tbody> </table> 

Я хочу, чтобы эта таблица показывала релевантные результаты для конференций, имеющих тот же PIN-код, что и пользовательский сеанс :: get («PIN»)

Вы можете комбинировать участников, используя GROUP_CONCAT

 SELECT conf.conference_sid, date_created, TIMEDIFF(date_completed, date_created) AS duration, conf.RecordURL, conf.PIN, GROUP_CONCAT(pid SEPARATOR ",") AS pid, GROUP_CONCAT(call_sid SEPARATOR ",") AS call_sid, GROUP_CONCAT(call_from SEPARATOR ",") AS call_from, GROUP_CONCAT(name_recording SEPARATOR ",") AS name_recording FROM conference conf LEFT OUTER JOIN participants p ON p.conference_sid = conf.conference_sid WHERE conf.PIN = 123 GROUP BY conf.conference_sid 

Обратитесь к документации SQLFIDDLE и MySQL о TIMEDIFF .

Теперь логика приложения будет

 <?php $pin = 123; $db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password'); $stmt = $db->prepare( 'SELECT conf.conference_sid, date_created, timediff(date_completed, date_created) AS duration, conf.RecordURL, conf.PIN, GROUP_CONCAT(pid SEPARATOR ",") AS pid, GROUP_CONCAT(call_sid SEPARATOR ",") AS call_sid, GROUP_CONCAT(call_from SEPARATOR ",") AS call_from, GROUP_CONCAT(name_recording SEPARATOR ",") AS name_recording FROM conference conf LEFT OUTER JOIN participants p ON p.conference_sid = conf.conference_sid WHERE conf.PIN = :pin GROUP BY conf.conference_sid'); $stmt->bindParam(':pin', $pin); ?> <table border="1"> <thead> <th>Date</th> <th>Duration</th> <th>Participants</th> <th>Recording</th> </thead> <tbody> <?php $stmt->execute(); while ($row = $stmt->fetch()) { ?> <tr> <td><?php echo $row['date_created']; ?></td> <td><?php echo $row['duration']; ?></td> <td> <table border="1"> <thead> <th>call_sid</th> <th>call_from</th> <th>name_recording</th> </thead> <tbody> <?php $length = count(explode(',', $row['pid'])); $call_sid = explode(',', $row['call_sid']); $call_from = explode(',', $row['call_from']); $name_recording = explode(',', $row['name_recording']); for ($i=0; $i < $length; $i++) { ?> <tr> <td> <?php echo $call_sid[$i]; ?> </td> <td> <?php echo $call_from[$i]; ?></td> <td> <?php echo $name_recording[$i]; ?> </td> <tr> <?php } ?> </tbody> </table> </td> <td> <a href="<?php echo $row['RecordURL']; ?>"> Call recording</a> </td> </tr> <?php } ?> </tbody> 

Вы получите набор результатов с запятыми (,) разделенными значениями в pid, call_sid, call_from и name_recording. Вы можете преобразовать эту строку в массив с помощью explode .

 array explode ( string $delimiter , string $string [, int $limit ] ) 

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

Я не буду делать часть PHP, поскольку я не настолько осведомлен в PHP, но вот SQL:

 SELECT * FROM `conference`, `participants` WHERE `conference`.PIN = $PIN AND `participants`.conference_sid = `conference`.conference_sid 

Это вернет строки с информацией из conference и participants этих conferences , объединенных в один ряд.

Следующий запрос предоставит вам необходимую информацию:

 SELECT c.conference_sid , c.date_created , timediff(c.date_completed, c.date_created) AS duration , p.call_from , p.name_recording , c.RecordURL FROM conference c JOIN participants p ON c.conference_sid = p.conference_sid WHERE c.PIN = :PIN ORDER BY c.conference_sid 

Вам нужно обработать результаты с помощью вложенного цикла. Внешняя петля должна увеличиваться каждый раз, когда изменяется conference_sid доступ. Внутренний цикл отобразит каждый элемент списка участников для этой конференции.

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

 <?php // assume $db is a PDO connection to the database /* @var $db PDO */ $q = 'SELECT conference_sid, date_created, date_completed, RecordURL, PIN' .' FROM conference'; // we need these $conferences = $db->query($q)->fetchAll(PDO::FETCH_CLASS,'stdClass'); // let's group them as CSV, and concatenate the contents with ":" $q = 'SELECT conference_sid,GROUP_CONCAT(CONCAT_WS(":",call_from,name_recording)) AS parts ' .' FROM participants GROUP BY conference_sid'; $conf_parts = array(); foreach ($db->query($q)->fetchAll(PDO::FETCH_CLASS,'stdClass') as $parts) { // save the participants as an array, their data is still joined though $conf_parts[$parts->conference_sid] = explode(',',$parts->parts); // their contents will be exploded later } ?> <table> <thead><th>Date</th><th>Duration</th><th>Participants</th><th>Recording</th></thead> <tbody><?php foreach ($conferences as $conference) { $csid = $conference->conference_sid; // http://stackoverflow.com/questions/3108591/calculate-number-of-hours-between-2-dates-in-php // Create two new DateTime-objects... $date1 = new DateTime($conference->date_completed); $date2 = new DateTime($conference->date_created); // The diff-methods returns a new DateInterval-object... $diff = $date2->diff($date1); ?><tr id="<?php echo $csid; ?>"> <td><?php echo $conference->date_created; ?></td> <td><?php echo $diff->format('H/i/s'); ?></td> <td> <ul><?php foreach ($conf_parts[$csid] as $participant) { // we have each participant for this conference call list ($call_from, $name_recording) = explode($participant,':'); // and now we have the required data from each participant ?><li><a href="<?php echo $name_recording ?>"><?php echo $call_from; ?></a></li><?php } ?></ul> </td> <td> <a href="<?php echo $conference->RecordURL; ?>">Call recording</a> </td> </tr><?php } ?></tbody> </table> 

В этом конкретном контенте я предпочитаю использовать два раздельных запроса. Вот как я это сделаю:

 <?php try { $db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password'); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { echo 'Could not connect to db'; exit; } $stmt_conferences = $db->prepare( 'SELECT date_created, timediff(date_completed, date_created) AS duration, RecordURL, conference_sid FROM conference WHERE PIN=:pin'); $stmt_conferences->bindParam(':pin', $pin); $stmt_participants = $db->prepare( 'SELECT name_recording, call_from FROM participants WHERE conference_sid=:confsid'); $stmt_participants->bindParam(':confsid', $confsid); ?> <table> <thead> <th>Date</th> <th>Duration</th> <th>Participants</th> <th>Recording</th> </thead> <tbody> <?php $pin = 1; /* get your PIN here */ $stmt_conferences->execute(); while ($row = $stmt_conferences->fetch()) { ?> <tr> <td><?php echo htmlspecialchars($row['date_created'], ENT_QUOTES); ?></td> <td><?php echo htmlspecialchars($row['duration'], ENT_QUOTES); ?></td> <td> <?php $confsid = $row['conference_sid']; $stmt_participants->execute(); while ($participant = $stmt_participants->fetch()) { ?> <li><a href="<?php echo htmlspecialchars($participant['name_recording'], ENT_QUOTES); ?>"> <?php echo htmlspecialchars($participant['call_from'], ENT_QUOTES); ?> </a> <?php } ?> </td> <td> <a href="<?php echo htmlspecialchars($row['RecordURL'], ENT_QUOTES); ?>"> Call recording</a> </td> </tr> <?php } ?> </tbody> - <?php try { $db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password'); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { echo 'Could not connect to db'; exit; } $stmt_conferences = $db->prepare( 'SELECT date_created, timediff(date_completed, date_created) AS duration, RecordURL, conference_sid FROM conference WHERE PIN=:pin'); $stmt_conferences->bindParam(':pin', $pin); $stmt_participants = $db->prepare( 'SELECT name_recording, call_from FROM participants WHERE conference_sid=:confsid'); $stmt_participants->bindParam(':confsid', $confsid); ?> <table> <thead> <th>Date</th> <th>Duration</th> <th>Participants</th> <th>Recording</th> </thead> <tbody> <?php $pin = 1; /* get your PIN here */ $stmt_conferences->execute(); while ($row = $stmt_conferences->fetch()) { ?> <tr> <td><?php echo htmlspecialchars($row['date_created'], ENT_QUOTES); ?></td> <td><?php echo htmlspecialchars($row['duration'], ENT_QUOTES); ?></td> <td> <?php $confsid = $row['conference_sid']; $stmt_participants->execute(); while ($participant = $stmt_participants->fetch()) { ?> <li><a href="<?php echo htmlspecialchars($participant['name_recording'], ENT_QUOTES); ?>"> <?php echo htmlspecialchars($participant['call_from'], ENT_QUOTES); ?> </a> <?php } ?> </td> <td> <a href="<?php echo htmlspecialchars($row['RecordURL'], ENT_QUOTES); ?>"> Call recording</a> </td> </tr> <?php } ?> </tbody> 

Обратите внимание, что вам нужно добавить код для обработки ошибок и правильно избежать всех данных, которые вы эхо (можете ли вы действительно доверять своей базе данных?). Идентификаторы элементов должны быть уникальными внутри всего документа, на вашей странице может быть только один id="conference_sid" . Вместо этого используйте классы.

редактировать

Если вы действительно можете доверять своей базе данных, вы можете просто выводить содержимое поля с кодом следующим образом:

 <?php echo $row['call_from']; ?> 

но что произойдет, если RecordURL содержит, например, следующую строку?

 <script>alert("I am injecting some code....");</script> 

Случилось бы, что на вашей странице будет введен какой-то нежелательный код, поэтому всегда лучше использовать безопасную функцию, такую ​​как htmlspecialchars (), каждый раз, когда вам нужно повторить вывод:

 <?php echo htmlspecialchars($row['call_from'], ENT_QUOTES); ?> 

таким образом, любой нежелательный код не будет вредным.

Я также добавил базовую конструкцию TRY / CATCH для обработки ошибок .

Надеюсь это поможет!

Во-первых, нам нужно получить наш результат.

 $vPIN = $_SESSION['PIN']; // or however you get your user's pin from session $vQuery = "SELECT * FROM conference AS a LEFT JOIN participants as B USING (conference_sid) WHERE a.PIN='$vPIN'"; $oResult = $oDB->execute($vQuery); $aRows = $oResult->fetchAll(PDO::FETCH_ASSOC); 

обратите внимание на префиксы: $ v, если для простой переменной $ o представляет собой ressource (который мне нравится думать как объект), $ a представляет массив. Это просто для моего умственного здравомыслия.

так что теперь у нас есть массив, вероятно, очень большой, содержащий каждую строку в таблице конференций раз каждую соответствующую строку в участниках. Сладкий, теперь давайте построим массив с некоторым значением в нем.

 foreach($aRows as $aRow) // maybe a bit confusing but the 's' changes everything: all rows vs one row {if (!isset($aConferences[$aRow['conference_sid']]['infos'])) {$aConferences[$aRow['conference_sid']]['infos']['date_created'] = $aRow['date_created']; $aConferences[$aRow['conference_sid']]['infos']['date_completed'] = $aRow['date_completed']; $aConferences[$aRow['conference_sid']]['infos']['record_url'] = $aRow['RecordURL']; $aConferences[$aRow['conference_sid']]['infos']['pin'] = $aRow['PIN'];} $aConferences[$aRow['conference_sid']]['participants'][] = $aRow['call_from'];} 

так что здесь происходит то, что для каждой строки, если информация для соответствующего конференц-центра не была установлена, они будут, а затем мы создадим список от 0 до x каждого call_from для этой конференции. print_r этого массива с фиктивными значениями:

 [1627]['infos']['date_created'] = 2013-11-26 ['date_completed'] = 2013-11-29 ['record_url'] = 'http://whatever.com' ['PIN'] = 139856742 ['participants'][0] = Bob [1] = gertrude [2] = Foo [8542]['infos']['date_created'] = 2013-12-01 ['date_completed'] = 2013-12-02 ['record_url'] = 'http://whateverelse.com' ['PIN'] = 584217 ['participants'][0] = Family Guy [1] = aragorn [2] = obama [3] = Loki 

так что вот хороший массив, с помощью которого мы можем построить таблицу html! Давайте сделаем это

 $vHTML = '<table> <thead> <th>Date</th> <th>Duration</th> <th>Participants</th> <th>Recording</th> </thead> <tbody>'; foreach ($aConferences as $conference_sid => $aConference) // notice the s and no s again {$vHTML.= '<tr id="' . $conference_sid . '">'; $vDateCreated = $aConference['infos']['date_created']; $vDateCompleted = $aConference['infos']['date_completed']; $vHTML.= '<td>' . $vDateCreated . '</td>'; $vHTML.= '<td>' . date('Ym-d',(strtotime($vDateCompleted) - strtotime($vDateCreated))) . '</td>'; // you might have to debug that date diff for yourself. $vHTML.= '<td><ul>'; // here a foreach for the participants foreach ($aConference['participants'] as $call_from) {$vHTML.= '<li>' . $call_from . '</li>';} $vHTML.= '</ul></td>'; $vHTML.= '<td>' . $aConference['infos']['record_url'] . '</td>'; $vHTML.= '</tr>';} $vHTML.= '</tbody></table>'; 

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

о, и не забудьте сделать что-то с $ vHTML. как echo $ vHTML 🙂