извлечение подкатегорий

Discussion in 'PHP' started by Seravin, 27 Jul 2010.

  1. Seravin

    Seravin Active Member

    Joined:
    25 Nov 2009
    Messages:
    475
    Likes Received:
    190
    Reputations:
    221
    Не знал как назвать тему, поэтому у темы корявое название. Я хотел рассказать тут о двух проблемах с которыми я столкнулся, и самые оптимальные пути их решения, которые я нашёл.

    Довольно частое явление, когда при в форме создаётся два селекта и при выборе в одном должны выбраться определённые данные в другом. У меня была задача такого плана: есть марки машин и модели каждой марки. Подгружать все модели и марки сразу нет смысла, т.к. страница будет требовать изрядно ресурсов. Я поступил так: создал в базе таблицу auto.
    ------------------------------------------------------------
    id | pid | name
    ------------------------------------------------------------

    затем создал скрипт чтобы записывать марки. В итоге у меня получилось что-то такое
    ------------------------------------------------------------
    id | pid | name
    ------------------------------------------------------------
    1 |0 | Audi
    2 |0 | BMW
    3 |0 | Dodge
    4 |0 | Ford
    5 |0 | Jaguar

    Затем я записал модели, линкуя их на марку через параметр pid

    ------------------------------------------------------------
    id | pid | name
    ------------------------------------------------------------
    1 |0 | Audi
    2 |0 | BMW
    3 |0 | Dodge
    4 |0 | Ford
    5 |0 | Jaguar
    6 |1 | 80
    7 |1 | 100
    8 |2 | X6
    ...

    Сделать это довольно просто.
    Затем я создал такую страничку с элементами:

    1) select и в него записал все строки с pid=0 (т.е. марки)
    Code:
    <select id="marka" name="marka">
       <option onclick="getmodel(this)" value="1">Audi</option>
       <option onclick="getmodel(this)" value="2">BMW</option>
       <option onclick="getmodel(this)" value="3">Dodge</option>
       <option onclick="getmodel(this)" value="4">Ford</option>
       <option onclick="getmodel(this)" value="5">Jaguar</option>
    </select>
    
    2) пустой select для моделей
    Code:
    <select id="marka" name="marka">
    </select>
    
    Ajax я плохо знаю, поэтому где-то на ачате давно брал скрипт для работы с ajax.

    Вот сами скрипты на странице:
    Code:
    var model = document.getElementById("model");
    
    function none() { };
    
    function request_model(url) {
      var r;
      if (window.XMLHttpRequest) {
        r = new XMLHttpRequest();
        r.onreadystatechange = none;
        r.open("GET", url, false);
        r.send(null);
        }
      else if (window.ActiveXObject) {
        r = new ActiveXObject("Microsoft.XMLHTTP");
        if (!r)
          r = new ActiveXObject("Msxml2.XMLHTTP");
        if (r) {
          r.onreadystatechange = none;
          r.open("GET", url, false);
          r.send(null);
          }
        }
      if (r.responseText!="") {
         eval(r.responseText);  
      }
      
      }
    
    
    function addOption(oListbox, text, value)
    {
      var oOption = document.createElement("option");
      oOption.appendChild(document.createTextNode(text));
      oOption.setAttribute("value", value);
    
      oListbox.appendChild(oOption);
    }
    
    function getmodel(item) {  
        while (model.options.length) {
            model.options[0] = null;
        }    
        if (document.getElementById("marka").selectedIndex!=0) {
        request_model('/getmodel.php?marka='+item.value);
    }
    }
    
    Вооот, остался только скрипт для извлечения моделей(getmodel.php):
    Code:
    if (isset($_GET['marka'])) {
            
            
           $db = new DB();       
           $db->dbquery("SELECT * FROM avto WHERE pid='".$db->secure($_GET['marka'])."'".$s);
           $res = $db->getresult();
           if ($res !== false) {
                while ($row = mysql_fetch_row($res)) {            
                echo 'addOption(model, "'.$row[2].'", "'.$row[0].'");';
                } 
                }       
           }
    
             
    }    
    
    P.S. у меня написан класс для работы с бд, но вообщем-то понятно как и что там.

    Собственно это и всё вроде. Я один раз написал и теперь просто видоизменяю, и согласен что немного коряво написано, но нужно было просто придумать как можно быстрее рабочую версию.




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

    Приведу пример для одного select'а для выбора кузова
    Code:
    <select name="kuzov"
    <option value="1">Бортовой грузовик</option>
    <option value="2">Внедорожник</option>
    <option value="3">Кабриолет</option><option value="4">Купе</option>
    <option value="5">Лимузин</option>
    <option value="6">Микроавтобус</option>
    <option value="7">Минивэн</option>
    <option value="8">Пикап</option>
    <option value="9">Седан</option>
    <option value="10">Универсал</option>
    <option value="11">Фургон</option>
    <option value="12">Хэтчбек</option>
    <option value="13">Грузовик</option>            
    </select>
    
    Мало того что надо было выводить в итоге название кузова, так ещё и выборку по нему нужно было делать. В итоге просто создавать кучу селектов было нелогично, т.к. потом меня бы конкретно бы это ограничило.

    И я снова вспомнил свой способ при работе с марками. Я создал таблицу parent
    ------------------------------------------------------------
    id | name
    ------------------------------------------------------------
    1 | kuzov
    2 | toplivo
    3 | strana
    ...

    и создал таблицу param

    ------------------------------------------------------------
    id | name | parent
    ------------------------------------------------------------
    1 | Бортовой грузовик | 1
    2 | Внедорожник | 1
    ....

    В итоге создав функцию для заполнения селекта
    Code:
    function getselnames($selname) {
                $db = new DB(); 
                $db->dbquery("SELECT * FROM parent WHERE name='".$db->secure($selname)."'");
                $result=$db->getstring();      
                if ($result!==false) {                                      
                    $db->dbquery("SELECT * FROM param WHERE parent='".$result[0]."'");
                    $result=$db->getresult();      
                    if ($result!==false) {  
                        while ($row = mysql_fetch_row($result))    {  
                            echo '<option value="'.$row[0].'">'.$row[1].'</option>';          
                        }
                    }
                }
                
        }
    
    Я заполнял селекты тремя строчками
    Code:
    <select name="kuzov">
     <?php   getselnames('kuzov');?>
    </select>
    
    что есть очень удобно.

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

    В итоге выборка приобрела такой вид:
    Code:
    if (isset($_GET['kuzov'])) {//проверка на то что селект есть в пост
    $res = mysql_query("SELECT * FROM param where parent=1 and id='".mysql_real_escape_string($_GET['kuzov'])."'");
    if (mysql_num_rows($res))
                        $where.=" and kuzov='".$db->secure($_GET['kuzov'])."'";
                    }
                }
    .....//таким образом проверяются все select'ы
    $adv = mysql_query("SELECT * FROM adv where 1".$where);//собственно выборка объяв, которая мне нужна была
    

    Коды немного сыровты, но цель была показать саму идею.

    P.S. тема была названа извлечение подкатегорий, т.к. таблица avto называлась category :)
     
    #1 Seravin, 27 Jul 2010
    Last edited: 27 Jul 2010
    1 person likes this.
  2. Gifts

    Gifts Green member

    Joined:
    25 Apr 2008
    Messages:
    2,494
    Likes Received:
    807
    Reputations:
    614
    1) в select можно обойтись одним аттрибутом onChange, вместо десятка onClick
    2) в пункте два можно обойтись одним селектом и/или избавиться от первой базы
    3) в последнем блоке кода совсем не понятно откуда и зачем это взялось
    4) лучше не использовать select * from, а перечислять требуемые столбцы
    5) хотелось бы услышать, какие и как следует в данном случае ставить индексы на таблицу
     
    _________________________
  3. Seravin

    Seravin Active Member

    Joined:
    25 Nov 2009
    Messages:
    475
    Likes Received:
    190
    Reputations:
    221
    1) согласен)
    2) насчёт одного селекта что-то не понял. А насчёт того чтобы обойтись без первой таблицы, как я понял parent, несоглашусь, т.к. форм много, а заполняешь ты их из админки, т.к. в любой момент надо чтото удалить/добавить. Можно оставить цифры конечно(типа для кузова 1, для топилва 2 и т.д.), но ты её заполнить просто не сможешь, сам подумай.
    3) последний щас распишу) на самом деле не понятно
    4) согласен
    5) напишу
     
  4. Gifts

    Gifts Green member

    Joined:
    25 Apr 2008
    Messages:
    2,494
    Likes Received:
    807
    Reputations:
    614
    Seravin 2) Как-то так. При этом не теряем время на отправку/обработку нового запроса
    Code:
    SELECT b.name as param_name FROM parent a LEFT JOIN param b ON a.id=b.parent WHERE a.name='kuzov'
    А избавиться - аналогично как и в первом все собрать в одну таблицу
    3) Все равно как-то мутно, не могу сформулировать но у меня какой то внутренний протест против такого кода
     
    _________________________
  5. Seravin

    Seravin Active Member

    Joined:
    25 Nov 2009
    Messages:
    475
    Likes Received:
    190
    Reputations:
    221
    3) )))) это то к чему пришёл я) одна голова хорошо а 15 лучше, естественно что всё это можно отточить, но я не мог особо на деталях зацикливаться. Когда буду всё оптимизировать, тогда уже и буду исправлять.

    2) насчёт этого вообщем то в 3) ответ