簡體   English   中英

使用一個查詢的結果進行另一個查詢並填充 HTML 選擇

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

編輯:將其移至頂部以避免混淆:

我對數據庫的結構沒有發言權或決定權,所以我必須使用我所擁有的。 否則,正如大多數評論所說,這個問題將通過規范化和清理表格來大大簡化。


我有一個數據庫表,其中包含汽車型號和一些“類別”(國產汽車、進口汽車、皮卡車、面包車等)。 值得注意的是,這些類別以整數代碼表示。

該表每行有 10 個類別列(Use1、Use2、Use3 等),因此每輛車最多可以有 10 個。

然后我有另一個表,其中包含與每個類別代碼對應的描述(例如,國產車 = 1,進口車 = 2,等等)。

我有一個包含兩個 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>

我需要根據汽車選擇的值填充類別選擇。 這是通過使用 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" }

表格截圖:

表格1表 2

請記住,這些是表和列的實際名稱,為了說明目的,我在上面的代碼中更改了它們。

foreach不起作用的原因是因為$result['categories']不是數組。 您查詢的select子句的重命名CodUse10categories

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

我不完全明白您是要顯示可用選項還是選定選項,但無論哪種方式,都會使用相同類型的多對多連接。 因此,您可能有一個名為available_options的表和另一個名為selected_options的表,而不是car_category


...但我不能更改數據庫!

在這種情況下,將沒有“正確”的解決方案; 只是一些創造性的方法來解決它。 目標應該是盡量減少產生的技術債務。 可能最大的部分是將模型(邏輯)與視圖(html)分開。 這樣,如果/當架構被更正時,您不必在更改訪問方法時深入視圖並破壞事物; 你所要做的就是插入一個不同的類或函數來獲取信息並以相同的方式輸出它。

定義視圖期望如何接收模型數據

在我開始使用模型之前,我需要知道視圖將如何使用它。 對於我自己的應用程序,我有一個類可以為我格式化輸入、下拉列表和復選框,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>

要使用它,我將提供一個關聯數組,其中 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,我的用法可能需要更正。

這里有很多錯誤,其中大部分可以通過檢查您的數據來解決。 使用var_dumpprint_r來確保變量包含您認為的內容。

首先,如果為數據庫列分配別名,則它只是一列。 所以你最終得到了數組索引CodUso1CodUso2 CodUso9 ,然后categories作為CodUso10的別名。 其次,您嘗試遍歷不存在的$result["categories"] ,因為fetchAll將返回一個索引數組。 這兩個你都可以通過使用print_r($result);來實現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);
    });
}

@kurisu請分享這兩個表的屏幕截圖(或結構)。 看來您在選擇時必須聯接表格。

我檢測到您的上層查詢有誤。 什么是CodUso值? 進一步說明。 在上層查詢中,類別將僅從CodUso10獲取值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM