使用一个查询的结果进行另一个查询并填充HTML选择

时间:2019-11-29 03:40:20

标签: php mysql pdo

编辑:将其移至顶部,以避免混淆:

我对数据库的结构没有发言权或决定权,所以我必须使用已有的知识。否则,如大多数评论所言,通过标准化和清理表可以大大简化此问题。


我有一个数据库表,其中包含汽车模型和一些“类别”(国家汽车,进口汽车,轻型货车,货车等)。需要注意的是,这些类别以整数代码表示。

此表每行有10个类别列(Use1,Use2,Use3等),因此每辆汽车最多可包含10个类别列。

然后我有另一个表,其中包含与每个类别代码相对应的描述(例如,国民汽车= 1,进口汽车= 2,依此类推)。

我有一个表单,其中包含两个HTML select元素,一个用于汽车,另一个用于类别。

<div class="form-group row">
    <label class="col-sm-2 form-control-label">Cars</label>
    <div class="col-sm-6">
        <select class="form-control" id="car_model" name="car_model" onchange="fetch_categories(this.value);">
            <option>Car 1</option>
            <option>Car 2</option>
            <option>Car 3</option>
        </select>
    </div>
</div>

<div class="form-group row">
    <label class="col-sm-2 form-control-label">Category</label>
    <div class="col-sm-4">
        <select class="form-control" name="category">
            <option>Select a car first...</option>
        </select>
    </div>
</div>

我需要根据选择的汽车的值来填充类别选择。这是通过使用Ajax(按预期工作并且可能与该主题无关的部分)完成的。

Ajax函数将汽车数据发送到php脚本,该脚本通过查询成功获取了所有相应类别。

我正在努力的是使用该查询的结果来通过另一个查询获取来自该查询的所有类别的描述。

我尝试做一个foreach来迭代查询1的所有结果,然后执行查询2,然后制定<option>标签,但是我做错了。

<?php
$car_model = $_POST['get_car'];

$query = "SELECT CodUso1, 
                CodUso2, 
                CodUso3, 
                CodUso4, 
                CodUso5, 
                CodUso6, 
                CodUso7, 
                CodUso8, 
                CodUso9, 
                CodUso10 AS categories FROM cars WHERE car_model = '$car_model'";
$stmt = $pdo->prepare($query);
$stmt->execute();
$result = $stmt->fetchAll();

foreach ($result['categories'] as $u):
    if ($result['CodUso1'] != 0 || 
        $result['CodUso2'] != 0 || 
        $result['CodUso3'] != 0 || 
        $result['CodUso4'] != 0 || 
        $result['CodUso5'] != 0 || 
        $result['CodUso6'] != 0 || 
        $result['CodUso7'] != 0 || 
        $result['CodUso8'] != 0 || 
        $result['CodUso9'] != 0 || 
        $result['CodUso10'] != 0) {
        $query = "SELECT description FROM categories WHERE code = '$u'";
        $stmt = $pdo->prepare($query);
        $stmt->execute();
        $result2 = $stmt->fetchAll();
?>      
        <option value="<?php echo $u ?>">
        <?php echo $result2; ?>
        </option>
<?php
}
endforeach;
exit;
?>

产生的<option>元素必须包含汽车代码作为值,并包含描述作为“描述”。

如果解释太复杂,我深表歉意,如果您需要进一步的信息或澄清,请告诉我。

编辑:第一个查询结果中的var_dump示例:

array(20) { ["CodUso1"]=> string(3) "101" [0]=> string(3) "101" ["CodUso2"]=> string(3) "502" [1]=> string(3) "502" ["CodUso3"]=> string(3) "305" [2]=> string(3) "305" ["CodUso4"]=> string(3) "406" [3]=> string(3) "406" ["CodUso5"]=> string(3) "103" [4]=> string(3) "103" ["CodUso6"]=> string(3) "508" [5]=> string(3) "508" ["CodUso7"]=> string(3) "455" [6]=> string(3) "455" ["CodUso8"]=> string(1) "0" [7]=> string(1) "0" ["CodUso9"]=> string(1) "0" [8]=> string(1) "0" ["coduso"]=> string(1) "0" [9]=> string(1) "0" }

表的屏幕截图:

Table 1 Table 2

请记住,这些是表和列的实际名称,出于说明目的,我在上面的代码中更改了它们。

2 个答案:

答案 0 :(得分:1)

foreach不起作用的原因是因为$result['categories']不是数组。您查询的select子句仅将CodUse10重命名为categories

SELECT CodUso1, 
            CodUso2, 
            CodUso3, 
            CodUso4, 
            CodUso5, 
            CodUso6, 
            CodUso7, 
            CodUso8, 
            CodUso9, 
           CodUso10 AS categories

但是真正的问题要深得多。它回到您的表架构。任何时候只要您有诸如field1,field2,field3之类的东西,……您几乎肯定会拥有未规范化的表。正确的架构如下所示:

Car    Car_Category   Category 
id     car_id         id 
model  category_id    description

如果要获取某辆汽车的类别列表,只需查询联接表:

select c.model, cat.id, cat.description
from car c
inner join car_category cc on c.id=cc.car_id
inner join category cat on cc.category_id=cat.id
where car.id=?

我不完全了解您是否尝试显示可用选项或 selected 选项,但是无论哪种方式,相同的多对多联接都会使用。因此,您可能有一个名为car_category的表和另一个名为available_options的表,而不是selected_options


...但是我无法更改数据库!

在这种情况下,将没有“正确”的解决方案;只是一些创造性的方法来解决它。目标应该是最大程度地减少所产生的技术债务。可能最大的部分是将模型(逻辑)与视图(html)分开。这样,如果/当模式被更正时,您不必在更改访问方法时深入视图并破坏事物。您要做的就是插入一个不同的类或函数,该类或函数获取信息并以相同的方式输出信息。

定义视图期望如何接收模型数据

在开始建立模型之前,我需要知道视图如何与该模型一起工作。对于我自己的应用程序,我有一个可以为我设置输入,下拉菜单和复选框格式的类,在轨上是ala ruby​​:

<div class="form-group">
  <label for="category">Category</label>
  <?= FormDecorator::showAsDropDown($optionList, $defaultOption,['id'=>'category','name'=>'category','class'=>'form-control', etc...]) ?>
</div>

要使用此功能,我将提供一个关联数组,其中options = array('value'=>'description')。关键是我需要模型来提供该数组,但是它可能会获取信息。但是该模型不负责创建任何 html。

使用相同的计划,您的输出看起来像

<div class="form-group row">
    <label class="col-sm-2 form-control-label">Category</label>
    <div class="col-sm-4">
        <select class="form-control" name="category">
            <option disabled selected hidden>Select a car first...</option>
            <?php foreach($options as $value => $description): ?>
              <option value="<?= $value ?>"><?= $description ?></option>
            <?php endforeach; ?>
        </select>
    </div>
</div>

创建一个模型以提供该数据

通常,我将使用对象,因为我使用自己的MVC框架。但是为简单起见,我将使用一个普通的旧函数(但我强烈建议使用类,因为它们更加灵活,并且方法名称在类的范围内,而不是全局范围内)

一次找到一个描述

您想遍历10个不同字段的结果,它们都方便地命名为CodUso{$number}

首先,制作一个函数检索原始数据:

function getCategoriesFor($car_model) {
    global $pdo; // ick, a class would avoid globals.
    $query =<<<CATEGORYQUERY
SELECT CodUso1, CodUso2, CodUso3, CodUso4, CodUso5, 
       CodUso6, CodUso7, CodUso8, CodUso9, CodUso10
FROM cars 
WHERE car_model = ?

CATEGORYQUERY;
    $stmt = $pdo->prepare($query);
    $stmt->execute($car_model);
    return stmt->fetch(\PDO::FETCH_ASSOC);
 }      

现在,创建一个函数来获取类别的描述:

// if this were an object, I would call it $Category->find($id)
function getCategoryDescriptionOf($code) {
    global $pdo;
    $query = "select * from categories where code=?";
    $stmt = $pdo->prepare($query);
    $stmt->execute($id);
    return $stmt->fetchAll();
}

然后创建一个功能,以遍历可用选项:

function getCategoryOptionsByModel($car_model) {

    // get the row containing CodUso1 ... CodUso10
    $categoryRow = getCategoriesFor($car_model);

    // always initialize output before generating its contents
    $out = [];

    // 10 fields, iterate through them.  This is the hacky part...
    for($i=1; $i <= 10; $i++) {

        // generate the field name
        $field = 'CodUso' . $i;

        // get the code from the car_model table row 
        $code = $categoryRow[$field];

        // format the array we will use in the view
        $out[$code] = getCategoryDescriptionOf($code);
    }
    return $out;  // [code => description]
}

哇!现在剩下的就是在视图中使用它:

<?php
$car_model = $_GET['car_model']; // or however it is assigned...

?>
<html> 
  ... snip ...

<div class="form-group row">
    <label class="col-sm-2 form-control-label">Category</label>
    <div class="col-sm-4">
        <select class="form-control" name="category">
            <option disabled selected hidden>Select a car first...</option>
            <?php foreach( getCategoryOptionsByModel($car_model) as $value => $description): ?>
              <option value="<?= $value ?>"><?= $description ?></option>
            <?php endforeach; ?>
        </select>
    </div>
</div>

万岁!我将表格标准化了!

如果您对表进行规范化,只需编写一个新函数来替换当前的getCategoriesFor()函数(必要时替换实字段名称),然后在视图中使用它而不是getCategoryOptionsByModel($car_model)或只是更改getCategoryOptionsByModel( $ car_model)返回getCategoriesFor($ car_model):

function getCategoriesFor($car_model) {
    global $pdo;
    $query =<<<THISISHOWITSHOULDBEDONEQUERY

select cat.code, cat.description
from cars c
inner join car_category cc on c.id=cc.car_id
inner join category cat on cc.category_id=cat.id
where cars.car_model=?

THISISHOWITSHOULDBEDONEQUERY;

    $stmt = $pdo->prepare($query)->execute($car_model);
    $out = [];
    foreach($stmt as $row) {
        $code = $row['code'];
        $description = $row['description'];
        $out[$code] = $description;
    }
    return $out;
}

好处

通过将逻辑与表示分离开来,并确保每个函数都做一件小事情,您现在使更改数据的编译方式变得更加容易。除了命名其他数据源之外,您不必对视图进行任何更改。如果您之后的下一个程序员是一个杀人狂,他知道您住在哪里,那么您会更加安全!

免责声明

这一切都离我远去。尽管我建议使用PDO,但我既不使用MySQL也不使用PDO,可能需要更正我的用法。

答案 1 :(得分:1)

这里有很多错误,其中大多数可以通过检查数据来解决。利用var_dumpprint_r确保变量包含您认为的内容。

首先,如果您为数据库列分配别名,那么它只是一列。因此,最后得到的是数组索引CodUso1CodUso2等到CodUso9,然后是categories作为CodUso10的别名。其次,您尝试遍历不存在的$result["categories"],因为fetchAll将返回索引数组。通过使用print_r($result);并了解您正在使用的工具,您可能已经意识到这两种情况。第三,将用户提供的数据直接传递到数据库查询中,这是使数据库受到攻击的好方法。

尝试一下。我们安全地准备了一条语句,该语句检索所有十个类别列,然后一次遍历它们,执行第二条准备好的语句以获取结果。请注意,除了安全性外,准备语句的好处之一是减少了重复执行同一查询时的开销。最后,尽可能将代码和演示文稿分开。在循环中间将HTML转储到屏幕是很丑陋的,而且更难处理。您应该返回一个JSON对象,然后使用Javascript回调构建元素。

<?php
$car_model = $_POST['get_car'];

// prepare the first query    
$query = "SELECT CodUso1, CodUso2, CodUso3, CodUso4, CodUso5,
        CodUso6, CodUso7, CodUso8, CodUso9, CodUso10
    FROM cars WHERE car_model = ? LIMIT 1";
$stmt = $pdo->prepare($query);
$stmt->execute([$car_model]);
// there's only one item, no loop needed
$categories = $stmt->fetch(\PDO::FETCH_ASSOC);

// here we prepare the second query
$query = "SELECT description FROM categories WHERE code = ?";
$stmt = $pdo->prepare($query);
// bind the variable to the ? parameter
$stmt->bindParam(1, $c);

$options = [];
foreach ($categories as $c) {
    if ($c == 0) {
        continue;
    }
    $stmt->execute();
    // we can fetch the single column directly
    $description = $stmt->fetchColumn();
    $options[] = ["id"=>$c, "description"=>$description];
}

header("Content-Type: application/json");
echo json_encode($options);

然后在您的Javascript回调中执行类似的操作。我假设您使用的是jQuery:

...
success: function(data, status, xhr) {
    // erase existing values
    $("select[name='category']").html("");
    // jQuery will turn this into an object automatically
    // loop through it and append an option element for each item
    data.each(function(v) {
        var sel = $("<option>").val(v.id).html(v.description);
        $("select[name='category']").append(sel);
    });
}