Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] Sortowanie elementów tablicy według określonej kolejności dla danego klucza
Forum PHP.pl > Forum > Przedszkole
by_ikar
Witam, mam pewien problem. Wytłumaczę go na zasadzie query buildera, żeby było bardziej jasne o co mi chodzi. Taki query builder dobrze jakby obsługiwał method chining, tak też można wtedy przykładowo użyć najpierw where, później select, a gdzieś na końcu from. Ogólnie można pomieszać kolejność. Załóżmy, że każde wywołanie jakiejś metody typu where, select, from itp dodaje do jednej większej tablicy swój klucz wraz z odpowiednimi wartościami. Jeżeli ktoś wie jak działa Zend_Db_Select, to mniej więcej zrozumie o co mi chodzi. I teraz w przypadku takiego buildera, na samym końcu wywołujemy jakąś metodę, która całość naszego zapytania skleja. W Zend_Db_Select, jest to foreach, który przelatuje po tablicy i dla odpowiednich kluczy wywołuje odpowiednie metody, które z tablicy pobierają dane i tworzą zapytanie.

Problem jaki ja mam, jest co w przypadku użycia innej kolejności? Powiedzmy użyjemy naszego buildera w taki sposób:

  1. $database->where($criteria)->offset(0)->select('*')->from('table')->get();


Jest to jak widać dość nietypowa konstrukcja zapytania, i można nawet przypadkiem, aczkolwiek nie tak jak w przykładzie, ale można zamienić kolejność niektórych rzeczy. Przykładowo użyć where za order i wtedy będziemy mieli niepoprawne zapytanie (w przypadku Zend_Db_Select jest to metoda assemble, która skleja sobie zapytanie).

Pomysłem na rozwiązanie mogłoby być posortowanie tablicy, za pomocą chociażby uksort, wraz z funkcją zwrotną. Teraz tylko pytanie, w jaki sposób określić konkretną kolejność dla konkretnych elementów, bo zapytanie może być różne, możemy użyć order, a nie musimy, to samo tyczy się where, limit, offset, group, union i tym podobnych. Pomyślałem że mogę w funkcji zwrotnej dla uksort, stworzyć kolejną tablicę, i do tej tablicy wrzucać elementy w określonej kolejności i dopiero ją zwrócić. Tylko mam właśnie problem, z wymyśleniem tego, w jaki sposób dana pętla miałaby wiedzieć ile tych elementów już jest w tablicy, oraz na jakie miejsce wstawić dany element..

Drugim pomysłem jest już podczas tworzenia zapytania, czyli podczas wywołania jakiejś metody, przykładowo where, już wtedy dodawana do tablicy jest wraz z swoją kolejnością. Aczkolwiek trzeba by trochę takiego buildera przebudować, i lepszą opcją byłoby sortowanie tuż przed sklejeniem zapytania..

Za wszelkie sugestie z góry dziękuje. Jeżeli napisałem coś nie jasno, to śmiało proszę pisać, z chęcią spróbuje to w jakiś inny bardziej zrozumiały sposób opisać.
qrzysztof
A czy nie prościej by tablica była asocjacyjna

  1. echo tablica['select'];
  2. echo tablica['where'];

itd.

Ja tak mam u siebie, a konkretnie to nie mam tablicy tylko odpowiednie właściwości w klasie.

A w metoda "sklejająca" jest u mnie trochę bardziej rozbudowana niż zwykłe foreach. Dzięki temu interfejs dla użytkownika jest dosyć elastyczny, a to jest chyba celem.
by_ikar
tablica jest asocjacyjna, stąd sortowanie uksort wink.gif nie chciałbym jej przerabiać na zwykłą tablicę żeby określić kolejność danej "metody". Pokaż jak możesz oczywiście, jak ty to zrobiłeś podczas sklejania takiej tablicy. Sam zrobiłem to póki co podobnie jak w Zend_Db_Select w metodzie assamble, czyli leci foreach po kluczach i dla podanych kluczy wywołuje odpowiednie metody. I to jest ok, o ile jest zachowana kolejność. I problem w sumie nie tylko w builderze teraz dostrzegam, ale i w kilku innych klasach, w których wykorzystuje method chining i w nich jest wymagana odpowiednia kolejność.. Stąd pomysł na sortowanie, które łatwiej zaimplementować w wielu klasach niż każdą osobno refaktoryzować..
qrzysztof
Podaj przykład bo nie do końca rozumiem skąd biorą się te problemy z kolejnością. Ja mam WHERE w $this->_where, a ORDER w $this->_order. I metoda budująca zapytanie sprawdzi sobie w odpowiednim miejscu czy zostało określone WHERE i ORDER i wstawi sobie to do zapytania, niezależnie od tego czy użytkownik wywołał:
  1. $db->select('*')->from('t_zamowienia')->where('id>%s', 100)->order('data DESC')->execute();


czy

  1. $db->select('*')->from('t_zamowienia')->order('data DESC')->where('id>%s', 100)->execute();


O taką koleność tu chodzi czy jeszcze o coś innego?
by_ikar
Tak, dokładnie o taką kolejność. Własnie dzisiaj przypadkowo zmieniłem kolejność przypadkowo where z order i oczywiście wygenerowało mi błąd i po sprawdzeniu ostatniego zapytania okazało się że zapytanie było źle sklejone. Szybko skorygowałem problem na poziomie modelu, ale na przyszłość chciałbym uniknąć tego typu problemów, dlatego wymyśliłem sobie posortowanie tablicy które mogłoby zdać tutaj egzamin.

Z tych przykładów które podałeś, załóżmy że każda metoda dodaje do tablicy odpowiedni klucz z odpowiednimi dla danej metody wartościami. Załóżmy że taka tablica wyglądała by tak:

Kod
Array
(
    [select] => count(*) as total
    [type] => select
    [from] => Array
        (
            [0] => Array
                (
                    [name] => gallery
                )

        )

    [where] => Array
        (
            [0] => Array
                (
                    [column] => cat
                    [type] =>
                    [bind] => cat
                )

        )

)


I jeżeli teraz przez taką tablicę przejadę pętlą foreach tworząc swoje zapytanie sql:

  1. $sql = '';
  2.  
  3. foreach(array_keys($this->query) as $part)
  4. {
  5. $method = '_render'.ucfirst($part);
  6. if(method_exists($this, $method))
  7. {
  8. $sql .= $this->$method();
  9. }
  10. }


To ta tablica w przypadku kiedy zawiera klucze w innej kolejności niż jest to wymagane, powstanie zapytanie które jest niepoprawne. A jeżeli leci jeden klucz po drugim taka pętla (co jest całkowicie normalne/oczywiste), to jeżeli bym odpowiednio posortował tablicę to ustrzegłbym się błędu niepoprawnej kolejności wywołanych metod klasy. Tylko właśnie problem w tym że nie mam pomysłu na to jak posortować taką tablicę wink.gif oczywiście sortowanie to nie jedyne rozwiązanie, i dlatego jestem na nie otwarty. Aczkolwiek sortowanie nie powiem, byłoby bardzo wygodne bo do kilku klasy wystarczyłoby prawdę mówiąc zmienić jedną linijkę i dodać gdzieś jedną klasę z metodą lub funkcję z tym sortowaniem i zmiana w innych klasach ograniczała by się do jednej linijki, czyli posortowania tablicy..

EDIT: wiem że mógłbym na sztywno ustawić tworzenie takiego zapytania podobnie jak jest tutaj: https://github.com/jstayton/QueryBuilder/bl...ueryBuilder.php

  1. /**
  2.   * Get the full query string.
  3.   *
  4.   * @param bool $usePlaceholders optional use ? placeholders, default true
  5.   * @return string full query string
  6.   */
  7. public function getQueryString($usePlaceholders = true) {
  8. $query = "";
  9.  
  10. // Only return the full query string if a SELECT value is set.
  11. if (!empty($this->select)) {
  12. $query .= $this->getSelectString();
  13.  
  14. if (!empty($this->from)) {
  15. $query .= " " . $this->getFromString();
  16. }
  17.  
  18. if (!empty($this->where)) {
  19. $query .= " " . $this->getWhereString($usePlaceholders);
  20. }
  21.  
  22. if (!empty($this->groupBy)) {
  23. $query .= " " . $this->getGroupByString();
  24. }
  25.  
  26. if (!empty($this->having)) {
  27. $query .= " " . $this->getHavingString($usePlaceholders);
  28. }
  29.  
  30. if (!empty($this->orderBy)) {
  31. $query .= " " . $this->getOrderByString();
  32. }
  33.  
  34. if (!empty($this->limit)) {
  35. $query .= " " . $this->getLimitString();
  36. }
  37. }
  38.  
  39. return $query;
  40. }


Że składane zapytanie jest przechowywane w określonych właściwościach klasy i następnie przy składaniu zapytania są one sprawdzane czy nie są puste i jeżeli nie są to dodawane są do zapytania. I tutaj jest jakby z góry określona ta kolejność. I mógłbym coś podobnego zrobić, z tym że wówczas musiałbym przebudować prawie całą klasę, a tego chcę uniknąć. Dlatego jest pomysł z tym sortowaniem, które mogłoby rozwiązać problem.
redeemer
A gdyby już w konstruktorze uzupełnić tablicę pustymi wartościami w odpowiedniej kolejności?
qrzysztof
Uzależnienie od kolejności wykonywania tych metod jest moim zdaniem bardzo problemogenne. Ale jeśli chcesz to robić w ten sposób to faktycznie najrozsądniej by było żeby ta tablica już istaniała z predefiniowanymi kluczami, ewentualnie inna tablica pomocnicza z kluczami, która byłaby wykorzystywana przy sortowaniu.

  1. $elements = array(
  2. 'select' => 'count(*) as total',
  3. 'type' => 'select',
  4. 'from' => array(
  5. 0 => array(
  6. 'name' => 'gallery'
  7. )
  8. ),
  9. 'where' => array(
  10. 0 => array(
  11. 'column' => 'cat',
  12. 'type' => '',
  13. 'bind' => 'cat'
  14. )
  15.  
  16. )
  17. );
  18.  
  19. function cmp($a, $b)
  20. {
  21. $order = array(
  22. 'type' => 0,
  23. 'select' => 1,
  24. 'from' => 2,
  25. 'where' => 3
  26. );
  27. return $order[$a] > $order[$b];
  28. }
  29.  
  30.  
  31. $sorting = uksort($elements, 'cmp');
by_ikar
Fakt, nie pomyślałem o tym. Właściwie to ja mam "drugą" taką tablicę w której są zawarte te klucze w odpowiedniej kolejności, z tą różnicą że służą one póki co do sprawdzenia do resetowania konkretnego "klucza", podobnie jak ma to miejsce w Zend_Db_Select. I właśnie się przyjrzałem i tam jest to zrobione tak, że nie tylko służy to do resetowania pojedyńczego klucza, ale całej tablicy.. I w sumie chyba jest to nawet lepsze od sortowania tablicy wink.gif dzięki panowie, nie pomyślałem o tym, i raczej bym się prędko nie domyślił, a rozwiązania dobre.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.