PHP内连接,当一个表有多个输出时使用foreach循环

时间:2012-12-01 00:12:20

标签: php mysql pdo

我有一种情况,我有一些桌子,我正在使用INNER JOIN。有一对多的关系,主表每个公园有一条线,但照片表可能有几条公园的线。我的代码的工作原理是为每个公园显示一张照片,但我只能显示一张照片。我怀疑问题是在foreach循环中,但我有点难过。这是代码:

try
{
    $sql = 'SELECT parks.id, parks.state, parks.name, parks.description, parks.site, parks.sname, parks.street, parks.city, parks.zip, parks.phone, comments.comment, comments.commentname, events.event, events.date, events.description2, photos.parkid, photos.type, photos.filename, photos.big FROM parks
            INNER JOIN comments INNER JOIN photos INNER JOIN events ON parks.parkid = comments.parkid and parks.parkid = photos.parkid and parks.parkid = events.parkid
            GROUP BY parks.id
            ORDER BY parks.name asc';
    $result = $pdo->query($sql);
}

catch (PDOException $e)
{
    $error = 'Error fetching data: ' . $e->getMessage();
    include 'output.html.php';
    exit();
}

//This is pulling the information from the database for display. On the foreach it will display each
//line until there are no more lines to display.    
foreach ($result as $row)
{
    $datas[] = array ('id' =>$row['id'],
                'parkid' =>$row['parkid'],
                'state' =>$row['state'], 
                'name' =>$row['name'], 
                'description' =>$row['description'], 
                'site' =>$row['site'], 
                'sname' =>$row['sname'],
                'street' =>$row['street'], 
                'city' =>$row['city'], 
                'phone' =>$row['phone'],
                'zip' =>$row['zip'],
                'commentname' =>$row['commentname'],
                'comment' =>$row['comment'],
                'event' =>$row['event'],
                'date' =>$row['date'],
                'description2' =>$row['description2'],
                'type' =>$row['type'],
                'filename' =>$row['filename'],
                'big' =>$row['big']);
}    
include 'writing.html.php';

<?php    
foreach ($datas as $name)
{
    if ($name['state'] === 'PA') 
    {
?>  
        <a href="#header" title="return to the top of the page">Back to top</a>
        <input type="hidden" name="id" value="' . $name['id'] . '" />
        <h1 id="name"> <?php echo ($name['name']) ?> </h1>
        <p id="descriptionlist">
            <?php echo ($name['description']) ?>
            <br />
            <ul id="link">
                <li class="l1">
                    <a href=<?php echo $name['site'] ?> target="_blank"> <?php echo $name['sname'] ?> </a>
                </li>
            </ul>
        </p>

        <h2>Location</h2>
        <div class = "loc"> 
            <p class="loct">
                <a class = "fancyImg" href="maps/<?php echo $name['id'] ?>state.gif"> <img src= "maps/<?php echo $name['id'] ?>state.gif"> </a>
                <br />
                <php echo ($name['street']) . ?>
                <br />
                <?php echo ($name['city']) .  
                    ($name['state']) .
                    ($name['zip']) ?>
                <br>
                <?php echo ($name['phone']) ?> 
                <br> <br>
            </p>
        </div>

        <h2>Trail Map</h2>
        <div class = "map">
            <p class = "mapt"> 
                Click to Enlarge
                <a class ="fancyImg" href= "/maps/<?php echo $name['id'] ?>maplink.gif">
                    <img src= "/maps/<?php echo $name['id'] ?>.gif"></a> <br> <br> 
            </p>
        </div>

        <h2>Photos</h2>
        <div class = "pho">
            <p class = "phot">
                <a class = "fancyImg" href= "/assets/indiv/<?php echo $name['big'] ?>.gif"> 
                    <img src= "<?php echo $name['filename'] ?>.gif"></a>**
                Submit <i>your</i> photos of <?php echo ($name['name']) ?> through our <ul id = "link"><li><a href="https://www.facebook.com/Ride4Wheel">Facebook Page!</li></ul></a></h3><p> Or go to our Contact Us page for information on how to e-mail us your favorite pictures! 
            </p>
        </div>

手头的问题在最后的pho div中。我希望$ name ['big']会给我这个循环的所有项目,但它只给我第一个。我在这里缺少一些基本的东西。

链接为http://www.ride4wheel.com/new_ma.php

4 个答案:

答案 0 :(得分:2)

关系数据库结果将始终作为行返回,除了您的查询具有一对多的关系之外,我认为在您的情况下,您将不得不使用您的唯一ID重新循环并查找“big”的不同值字段。

我也不认为你需要foreach循环来使结果像关联数组一样,你可能需要使用它来代替:PDOStatement::fetchAll

答案 1 :(得分:2)

问题出在您的查询中。 您INNER JOIN张照片会为所有照片创建一行,并为每个指定的照片重复公园。 GROUP BY再次区分公园,但迫使MySQL选择其中一张指定的照片。

如果一个公园没有图片,它根本就不在列表中(INNNER JOIN =给我所有在两个表格中都有关系的图片)

您可以删除GROUP BY并将INNER JOINs替换为LEFT JOINs(给我所有公园并附上图片和评论,如果有的话),这需要在输出循环中进行检查(我目前的公园,我是否已经显示这个公园,显示当前图片,显示当前评论)

更干净的但更慢的解决方案是删除连接和字段以获取图像和注释,这样你才能获得公园;然后在公园循环中获取评论并在两个额外的查询中获取当前公园的图像。

编辑1:

就像在评论中所说的那样,这不是一个非常好的选择,因为你将触发两个额外的查询并为你添加的每个公园再运行两个循环。我希望您了解多对多关系的基本问题 - 数据库结果只能是二维表

编辑2:

我已将代码修改为我建议的内容,请注意

<?php
try
{
    $sql = 'SELECT 
                parks.id, 
                parks.state, 
                parks.name, 
                parks.description, 
                parks.site, 
                parks.sname, 
                parks.street, 
                parks.city, 
                parks.zip, 
                parks.phone, 
                comments.comment, 
                comments.commentname, 
                events.event, 
                events.date, 
                events.description2,                
                photos.type, 
                photos.filename, 
                photos.big 
            FROM 
                parks
                    LEFT JOIN comments ON comments.parkid = parks.id
                    LEFT JOIN photos ON photos.parkid = parks.id
                    LEFT JOIN events ON events.parkid = parks.id
            ORDER BY 
                parks.name ASC';
    $result = $pdo->query($sql);
}
catch (PDOException $e)
{
    $error = 'Error fetching data: ' . $e->getMessage();
    include 'output.html.php';
    exit();
}

//This is pulling the information from the database for display. On the foreach it will display each
//line until there are no more lines to display.    
$datas = array();

foreach ( $result as $row )
{
        // we didn't add this park yet
        if ( !array_key_exists( $row['id'], $datas )
        {
            $datas[$row['id']] = array (
                'id' => $row['id'],                
                'state' => $row['state'], 
                'name' => $row['name'], 
                'description' => $row['description'], 
                'site' => $row['site'], 
                'sname' => $row['sname'],
                'street' => $row['street'], 
                'city' => $row['city'], 
                'phone' => $row['phone'],
                'zip' => $row['zip'],
                'comments' => array(),
                'photos' => array(),
                'events' => array()
            );
        }

        // if there is no comment for this park, this will be null
        if ( $row['comment'] )
        {
            $datas[$row['id']]['comments'][] = array (
                'comment' => $row['comment'],
                'commentname' => $row['commentname']
            );
        }

        // same for photos
        if ( $row['filename'] )
        {
            $datas[$row['id']]['photos'][] = array (                
                'type' => $row['type']
                'filename' => $row['filename']
                'big' => $row['big']
            );
        }

        // same for events
        if ( $row['event'] )
        {
            $datas[$row['id']]['events'][] = array (
                'event' => $row['event'],
                'date' => $row['date'],
                'description2' => $row['description2']
            );
        }
}

include 'writing.html.php';

和writing.html.php

<?php    
foreach ($datas as $park)
{
    // do you only want to display PA? 
    // then add " WHERE state = 'PA' " to your query
    if ($park['state'] === 'PA') 
    {
?>  
        <a href="#header" title="return to the top of the page">Back to top</a>
        <input type="hidden" name="id" value="' . $park['id'] . '" />
        <h1 id="name"> <?php echo ($park['name']) ?> </h1>
        <p id="descriptionlist">
            <?php echo ($park['description']) ?>
            <br />
            <ul id="link">
                <li class="l1">
                    <a href=<?php echo $park['site'] ?> target="_blank"> <?php echo $park['sname'] ?> </a>
                </li>
            </ul>
        </p>

        <h2>Location</h2>
        <div class = "loc"> 
            <p class="loct">
                <a class = "fancyImg" href="maps/<?php echo $park['id'] ?>state.gif"> <img src= "maps/<?php echo $park['id'] ?>state.gif"> </a>
                <br />
                <php echo ($park['street']) . ?>
                <br />
                <?php echo ($park['city']) .  
                    ($park['state']) .
                    ($park['zip']) ?>
                <br>
                <?php echo ($park['phone']) ?> 
                <br> <br>
            </p>
        </div>

        <h2>Trail Map</h2>
        <div class = "map">
            <p class = "mapt"> 
                Click to Enlarge
                <a class ="fancyImg" href= "/maps/<?php echo $park['id'] ?>maplink.gif">
                    <img src= "/maps/<?php echo $park['id'] ?>.gif"></a> <br> <br> 
            </p>
        </div>
        <?php if ( !empty( $park['photos'] ) ): ?>
        <h2>Photos</h2>
            <?php foreach( $park['photos'] as $photo ): ?>
                <div class = "pho">
                    <p class = "phot">
                        <a class = "fancyImg" href= "/assets/indiv/<?php echo $photo['big'] ?>.gif"> 
                            <img src= "<?php echo $photo['filename'] ?>.gif"></a>**
                        Submit <i>your</i> photos of <?php echo ($photo['name']) ?> through our <ul id = "link"><li><a href="https://www.facebook.com/Ride4Wheel">Facebook Page!</li></ul></a></h3><p> Or go to our Contact Us page for information on how to e-mail us your favorite pictures! 
                    </p>
                </div>
            <?php endforeach; ?>
        <?php endif; ?>

        <?php if ( !empty( $park['comments'] ) ): ?>
            <h2>Comments</h2>
            <?php foreach( $park['comments'] as $comment ): ?>
                <?php echo $comment['comment']; ?>
            <?php endforeach; ?>
        <?php endif; ?>

        <?php if ( !empty( $park['events'] ) ): ?>
            <h2>Events</h2>
            <?php foreach( $park['events'] as $event ): ?>
                <?php echo $event['event']; ?>
            <?php endforeach; ?>
        <?php endif; ?>
<?php 
    }
}

编辑3:

您需要了解的是,您只能从数据库返回二维结果表。那么如果你的公园不会被重复,你将如何为一个公园返回10张照片?是的,那不是(干净地)可能的。这就是为什么你必须过滤重复的行,只有在你停车后拍摄照片,评论和事件。

在上面的代码中,我使用唯一的park id作为数组索引,这样我就可以确定这个park是否已经在数据数组中,然后添加照片,事件和注释。

如果您执行print_r( $results )并将其与print_r( $datas )进行比较,您将了解整个事情

答案 2 :(得分:1)

除非每个公园至少有一条评论和一张照片,否则您的查询将不会返回所有公园的行。这就是INNER JOIN的工作原理,它只根据您指定的连接条件获取匹配的所有行的交集。

如果您想显示所有公园,无论他们是否有评论和/或照片,我建议您使用LEFT JOIN代替:

SELECT
    parks.id,
    parks.state,
    parks.name,
    parks.description,
    parks.site,
    parks.sname,
    parks.street,
    parks.city,
    parks.zip,
    parks.phone,
    comments.comment,
    comments.commentname,
    events.event,
    events.date,
    events.description2,
    photos.parkid,
    photos.type,
    photos.filename,
    photos.big
FROM
    parks
    LEFT JOIN comments ON (parks.parkid = comments.parkid)
    LEFT JOIN photos ON (parks.parkid = photos.parkid)
    LEFT JOIN events ON (parks.parkid = events.parkid)
ORDER BY
    parks.name asc

修改

我删除了GROUP BY条款,因为我很确定这不是你想要的;您可能希望查看与公园相关的每张照片或评论,而不是将它们全部任意缩减为每个公园的一行。

修改

使用此查询可以解决原始查询可能遇到的任何问题,而不会返回您期望评论和照片的行。你曾经说过这段关系是1公园到很多评论和许多照片。此查询应返回与每个公园相关联的每条评论和照片,但每个公园将返回多行。您可能需要调整代码来补偿这一点,可能是通过创建一个公园数据数组,其中数组中的每个元素对应一个公园。每个元素还应包含一系列评论和照片,每个公园可能有多个评论和照片。

应该准备结果集上的每次迭代以在park数据数组中创建一个新元素,或者识别当前结果集行对应于您已经开始构建其数据的公园。此外,如果当前迭代中有任何评论或照片,则应将其添加到当前(新的或现有的)公园的评论和/或照片数组中。以下是如何执行此操作的示例foreach循环:

$parks = array();

foreach ($result as $row) {
    // Create a new element in $parks because it's not in the $parks array yet
    if (! array_key_exists($row['id'], $parks)) {
        $park = array();
        $park['id'] = $row['id'];
        $park['state'] = $row['state'];
        $park['name'] = $row['name'];
        $park['description'] = $row['description'];
        $park['site'] = $row['site'];
        $park['sname'] = $row['sname'];
        $park['street'] = $row['street'];
        $park['city'] = $row['city'];
        $park['zip'] = $row['zip'];
        $park['phone'] = $row['phone'];

        $park['comments'] = array();
        $park['photos'] = array();

        $parks[$row['id']] = $park;
    } else {
        // Otherwise, this is a park we've already seen
        $park = $parks[$row['id']];
    }

    // If there are comments in this result set row, add them
    if ($row['comment'] || $row['commentname']) {
        $park['comments'][] = array(
            'comment' => $row['comment'],
            'commentname' => $row['commentname']
        );
    }

    // If there are photos in this result set row, add them
    if ($row['type'] || $row['filename'] || $row['big']) {
        $park['photos'][] = array(
            'type' => $row['type'], 
            'filename' => $row['filename'], 
            'big' => $row['big']
        );
    }
}

这段代码不是那么漂亮或很好的因素,它肯定可以改进。我只是匆匆把它扔到一起,给你一个如何构建这样一个结构的例子。

关系数据并不总是干净地映射到分层对象图,这是object-relational impedance mismatch

答案 3 :(得分:0)

要获取一个数组中的所有行而不是第一个foreach循环,请尝试使用<?$datas = $pdo->fetchAll();?>