Autor Zpráva
Džůny
Profil
Ahoj, mám v DB následující tabulku kategorií:

id  parent  name
1   0       Dobroty
2   0       Ovoce
3   1       Čokoláda
4   1       Lízátko
5   2       Jablko
6   0       Zelenina

Vysvětlení: parent = 0 značí základní kategorii, parent = n > 0 značí, že jde o podkategorii kategorie s id n.

Potřebuji to nacpat do Nette SelectBoxu, tj. potřebuji z toho udělat vícerozměrné asociativní pole:

Array (    
    'Dobroty' => array(
        '3' => 'Čokoláda',
        '4' => 'Lízátko',
    ),
    'Ovoce' => array(
        '5' => 'Jablko',
    ),
    'Zelenina' => array(),
);

Nějakým foreachovým bastlem bych se určitě výsledku dobral, ale otázky, které mi pořád lítají hlavou jsou dvě:

1) Nepřehlédl jsem některou z možností SQL, která by mi tohle "setřídění" umožnila už na úrovni DB?
2) Nemá už SPL / Nette nějaké konstrukty, které by tohle umožnily udělat?

Nechci zkrátka vymýšlet znovu kolo, nebo to psát "škaredě" (víc oddělených dotazů, iterování, ověřování, pushování do pole).
Ale je taky možné, že chci od jazyků moc a jiný způsob jak to udělat není? :)

Zadání do DB "natvrdo" nepřipadá v úvahu, protože a) je to principiálně špatně :) a za b) kategorie se budou v administraci dynamicky přidávat a odebírat.

Díky všem za přečtení a případné nápady.
tiso
Profil
Pre ORDER BY parent, id je to spracovanie celkom triviálne, čo na tom chceš zjednodušovať?
Keeehi
Profil
Jestli jde jen takhle o dvě úrovně, tak jde přece o klasickou vazbu 1:N. Pak jde o špatný návrh databáze a mělo by to být rozděleno do dvou tabulek. Pokud však jde doopravdy o strom kde předem nevíte, jak může být hluboký, pak je to správně.
Juraj Hajdúch
Profil
Kedysi dávno som si napísal toto:
class StructuredDiscusion{

        public $v_id_key = "id"; // názov stĺpca s ID
        public $v_pid_key = "pid"; // názov stĺpca Parentid
      public $v_parent_sign = 0; // označenie pre ROOT
        protected $v_result = array();
    private $v_level = 0;
        private $v_parent = 0;

      public function setIDKey($i_key){
        $this->v_id_key = $i_key;
        return $this;
    }

        public function setPIDKey($i_key){
        $this->v_pid_key = $i_key;
                return $this;
    }

        public function setParentSign($i_sign){
        $this->v_parent_sign = $i_sign;
                return $this;
    }

    public function checkDiscusion($i_array, $i_parent = 0){
      $h_status = (boolean) false;
      foreach($i_array as $h_key => $h_value){
        if($i_array[$h_key][$this->v_pid_key] == $i_parent){
          $i_array[$h_key]["level"] = $this->v_level;
          $this->result[] = $i_array[$h_key];
          $h_status = true;
          if($h_status == true){
            $this->v_level++;
            self::checkDiscusion($i_array, $i_array[$h_key][$this->v_id_key]);
            $this->v_level--;
          }
        }
      }
    }

}
Džůny
Profil
Otázku jsem položil s cílem vyhnout se přesně něčemu takovému. :)

Rozhodně půjde o více než dvě úrovně, proto je to v databázi řešeno takto.

tiso, napadá tě jednodušší implementace než to, čím přispěl Juraj Hajdúch?
tiso
Profil
Niečo takéto:
$out = array();
$firstLevel = array();
while ($row = mysql_fetch_assoc($result)) {
    if ($row['parent'] == 0) {
        $out[$row['name']] = array();
        $firstLevel[$row['id']] = $row['name'];
    } else {
        $out[$firstLevel[$row['parent']]][$row['id']] = $row['name'];
    }
}
Džůny
Profil
Úplně jsem na tohle téma zapomněl. tiso, tvoje řešení funguje jen pro dvě úrovně.

Dám sem moje řešení, kdyby to někdo v budoucnu potřeboval:

        $categories = $this->categoryFacade->getAll()->order('parent')->fetchAll();

        $output = Array();
        foreach ($categories as $category)
        {
            /* non-root categories > 0 */
            if ($category['parent'])
            {
                break;
            }
            
            /* build nested array */
            $output[$category['slug']] = $this->findChildren($categories, $category);
        }

    private function findChildren($categories, $parent)
    {
        $subcategory = Array();
        foreach ($categories as $category)
        {
            /* parent found */
            if ($category['parent'] == $parent['id'])
            {
                $subcategory[$category['id']] = $this->findChildren($categories, $category);
            }
        }
        return empty($subcategory) ? $parent->slug : $subcategory;
    }

Mělo by to fungovat pro libovolných n zanoření, pokud budou categories setříděné podle pole parent. Za výkon ale ruku do ohně vzhledem k rekurzi nedám. :D Možná si zkusím, pro tu srandu, vygenerovat nějaké obří nested pole, projít ho a odhadnout složitost.
tiso
Profil
Džůny: vychádzal som z príkladu, ktorý si tu dal a tvoju neskoršiu poznámku o viacerých úrovniach som si nevšimol. Ako má vyzerať to výsledné pole pre viac úrovní?

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: