简体   繁体   English

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

[英]Using the results of one query to make another query and populate an HTML select

Edit: will move this to the top to avoid confusion:编辑:将其移至顶部以避免混淆:

I have no say or power of decision on the database's structure, so I must work with what I've got.我对数据库的结构没有发言权或决定权,所以我必须使用我所拥有的。 Otherwise, as most comments stated this issue would be simplified greatly by normalizing and cleaning up the tables.否则,正如大多数评论所说,这个问题将通过规范化和清理表格来大大简化。


I've got a database table which contains car models and some "categories" (national car, imported car, pick-up truck, van, etc).我有一个数据库表,其中包含汽车型号和一些“类别”(国产汽车、进口汽车、皮卡车、面包车等)。 Something of note is that these categories are represented in integer codes.值得注意的是,这些类别以整数代码表示。

This table has 10 category columns for each row (Use1, Use2, Use3, etc), so each car can have up to 10 of them.该表每行有 10 个类别列(Use1、Use2、Use3 等),因此每辆车最多可以有 10 个。

Then I have another table which contains the descriptions corresponding to each category code (eg national car = 1, imported car = 2, and so on).然后我有另一个表,其中包含与每个类别代码对应的描述(例如,国产车 = 1,进口车 = 2,等等)。

I have a form which contains two HTML select elements, one for cars and the other for categories.我有一个包含两个 HTML 选择元素的表单,一个用于汽车,另一个用于类别。

<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>

I need to populate the category select based on the value of the cars select.我需要根据汽车选择的值填充类别选择。 This is being done by using Ajax (part which works as intended and is probably irrelevant to the subject).这是通过使用 Ajax 完成的(按预期工作的部分,可能与主题无关)。

The Ajax function sends the car data to a php script which successfully gets all of it's corresponding categories through a query. Ajax 函数将汽车数据发送到一个 php 脚本,该脚本通过查询成功获取了所有相应的类别。

What I'm struggling with is using the results from that query to fetch the descriptions of all the categories that came from it through another query.我正在努力使用该查询的结果来获取通过另一个查询来自它的所有类别的描述。

I've tried doing a foreach to iterate all results from query #1 and then executing query #2 then formulating the <option> tags, but I'm not getting it right.我试过做一个 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;
?>

The resulting <option> elements must contain the car code as value and the description as it's "description".结果<option>元素必须包含汽车代码作为值和描述作为“描述”。

I apologize if the explanation is too convoluted, if you need any further information or clarification please let me know.如果解释过于复杂,我深表歉意,如果您需要任何进一步的信息或澄清,请告诉我。

Edit: example of var_dump from the first query's result:编辑:来自第一个查询结果的 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" }

Screenshots of tables:表格截图:

表格1表 2

Keep in mind these are the actual names of the tables and columns, I changed them in the above code for illustration purposes.请记住,这些是表和列的实际名称,为了说明目的,我在上面的代码中更改了它们。

The reason the foreach is not working is because $result['categories'] is not an array. foreach不起作用的原因是因为$result['categories']不是数组。 Your query's select clause simply renames CodUse10 to categories .您查询的select子句的重命名CodUse10categories

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

But the real issue goes deeper than that.但真正的问题远不止于此。 It goes back to your table schema.它可以追溯到您的表架构。 Any time you have things like field1, field2, field3, ... you almost certainly have a table that is not normalized.任何时候你有像 field1、field2、field3 之类的东西……你几乎肯定有一个没有规范化的表。 The proper schema would look something like this:正确的架构看起来像这样:

Car    Car_Category   Category 
id     car_id         id 
model  category_id    description

When you want to get a list of categories a certain car has, simply query the joined tables:当您想获取某辆车的类别列表时,只需查询连接表:

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=?

I don't completely understand if you were trying to display available options or selected options, but either way, the same kind of many-to-many join would be used.我不完全明白您是要显示可用选项还是选定选项,但无论哪种方式,都会使用相同类型的多对多连接。 So instead of car_category you might have a table called available_options and another called selected_options .因此,您可能有一个名为available_options的表和另一个名为selected_options的表,而不是car_category


...But I can't change the database! ...但我不能更改数据库!

In this case, there will be no "right" solution;在这种情况下,将没有“正确”的解决方案; just some creative ways to work around it.只是一些创造性的方法来解决它。 The goal should be to minimize the amount of technical debt created.目标应该是尽量减少产生的技术债务。 Probably the biggest part is to separate model (logic) from the view (html).可能最大的部分是将模型(逻辑)与视图(html)分开。 That way, if/when the schema is corrected, you don't have to dig into the view and break things as you change access methods;这样,如果/当架构被更正时,您不必在更改访问方法时深入视图并破坏事物; all you have to do is plug in a different class or function that gets the information and outputs it the same way.你所要做的就是插入一个不同的类或函数来获取信息并以相同的方式输出它。

Define how the view expects to receive the model's data定义视图期望如何接收模型数据

Before I start in on the model, I need to know how the view will work with it.在我开始使用模型之前,我需要知道视图将如何使用它。 For my own applications, I have a class which formats inputs, dropdowns, and checkboxes for me, ala ruby on rails:对于我自己的应用程序,我有一个类可以为我格式化输入、下拉列表和复选框,ala ruby​​ on rails:

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

To use this, I would provide an associative array where options = array('value'=>'description').要使用它,我将提供一个关联数组,其中 options = array('value'=>'description')。 The point is that I need the model to provide that array, however it may go about getting the information.关键是我需要模型来提供该数组,但是它可能会获取信息。 But the model is not responsible for creating any html.但该模型负责创建任何html。

Using this same plan, your output could look like使用相同的计划,您的输出可能看起来像

<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>

Create a model to provide that data创建一个模型来提供该数据

Normally, I would use an object since I use my own MVC framework.通常,我会使用一个对象,因为我使用我自己的 MVC 框架。 But for simplicity, I'll use a plain old function (but I highly recommend using classes as they are much more flexible, and method names are in the class's scope, not global scope)但为了简单起见,我将使用一个普通的旧函数(但我强烈建议使用类,因为它们更灵活,并且方法名称在类的范围内,而不是全局范围内)

Find description one at a time一次查找一个描述

You want to loop through the results of 10 different fields, all conveniently named CodUso{$number} .您想遍历 10 个不同字段的结果,所有字段都方便地命名为CodUso{$number}

First, make a function to retrieve the raw data:首先,创建一个函数来检索原始数据:

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

Now, make a function to get descriptions of the categories:现在,创建一个函数来获取类别的描述:

// 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();
}

Then make a function to iterate through the options available:然后创建一个函数来迭代可用的选项:

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

Whew!哇! now all that's left is to use it in the view:现在剩下的就是在视图中使用它:

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

Hurray!欢呼! I normalized the tables!我对表格进行了标准化!

If you normalize the tables, just write a new function to replace the current getCategoriesFor() function (substitute real field names as necessary) and use it in the view instead of getCategoryOptionsByModel($car_model) or just change getCategoryOptionsByModel($car_model) to return getCategoriesFor($car_model):如果您对表进行规范化,只需编写一个新函数来替换当前的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;
}

Benefits好处

By separating logic from presentation, and making sure each function does one small thing, you've now made it easier to change how the data is compiled.通过将逻辑与表示分离,并确保每个函数只做一件小事,您现在可以更轻松地更改数据的编译方式。 You shouldn't have to make any changes to the view, other than to name a different data source.除了命名不同的数据源外,您不必对视图进行任何更改。 If the next programmer after you is a homicidal maniac who knows where you live, you'll be a lot safer!如果你之后的下一个程序员是一个知道你住在哪里的杀人狂,你会安全很多!

Disclaimer免责声明

This is all off the top of my head.这一切都在我的脑海中。 Although I recommend PDO, I don't use MySQL nor PDO and my usage may need to be corrected.虽然我推荐 PDO,但我不使用 MySQL 也不使用 PDO,我的用法可能需要更正。

Many mistakes here, most of which could be figured out by inspecting your data.这里有很多错误,其中大部分可以通过检查您的数据来解决。 Make use of var_dump and print_r to ensure that a variable contains what you think it does.使用var_dumpprint_r来确保变量包含您认为的内容。

First, if you assign an alias to a database column, it's just one column.首先,如果为数据库列分配别名,则它只是一列。 So you were ending up with array indices CodUso1 , CodUso2 , etc through to CodUso9 , and then categories as an alias to CodUso10 .所以你最终得到了数组索引CodUso1CodUso2 CodUso9 ,然后categories作为CodUso10的别名。 Second, you try looping through $result["categories"] which doesn't exist, because fetchAll will return an indexed array.其次,您尝试遍历不存在的$result["categories"] ,因为fetchAll将返回一个索引数组。 Both these you could have realized by using print_r($result);这两个你都可以通过使用print_r($result);来实现print_r($result); and taking a look at what you're working with.并查看您正在使用的内容。 Third, you're passing user-supplied data directly into the database query, which is a good way to get your database attacked.第三,您将用户提供的数据直接传递到数据库查询中,这是让您的数据库受到攻击的好方法。

Try this out.试试这个。 We safely prepare a statement that retrieves all ten category columns, and then loop through them one at a time, executing a second prepared statement to get the results.我们安全地准备一个检索所有十个类别列的语句,然后一次一个循环,执行第二个准备好的语句以获取结果。 Note one of the benefits of prepared statements, aside from security, is reduced overhead when repeatedly executing the same query.请注意,除了安全性之外,准备好的语句的好处之一是在重复执行同一查询时减少了开销。 Finally, separate your code and your presentation as much as you can.最后,尽可能将代码和演示文稿分开。 Dumping HTML to screen in the middle of a loop is ugly and harder to work with.将 HTML 转储到循环中间的屏幕是丑陋且难以使用的。 You should be returning a JSON object and then using your Javascript callback to build the elements.您应该返回一个 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);

Then in your Javascript callback, do something like this.然后在你的 Javascript 回调中,做这样的事情。 I'm assuming you're using jQuery:我假设您正在使用 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);
    });
}

@kurisu please share screenshots(or structure) of those two tables. @kurisu请分享这两个表的屏幕截图(或结构)。 it looks like you have to JOIN the tables when selecting. 看来您在选择时必须联接表格。

Your upper query is wrong as I detected. 我检测到您的上层查询有误。 What are CodUso values? 什么是CodUso值? Explain further. 进一步说明。 In your upper query, categories will get values only from CodUso10. 在上层查询中,类别将仅从CodUso10获取值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM