Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] router na przykładzie apple.com
Forum PHP.pl > Forum > Przedszkole
Zxcy
Czy ktoś może orientuje się jak jest zaprogramowany ruter witryny apple.com?

Domyślam się że wszytko idzie przez index.php, sprawdza czy pierwsze 2 znaki, to nie język w URI i właśnie co dalej... Jak działa ten mechanizm że kontroler wie iż /macos/ to osobna strona, a później /macos/safari/ to jej podstrona?
lobopol
poczytaj o "ładnych url", nice url. Popatrz na rozwiązania np. w symfony/kohanie/zendzie i autoloadzie
Gość
Wiem czym są ładne urle i jak je stosować, każdy FW odwołuje się do /kontrolera/metody/wartości ... w przykładzie apple tego nie ma, dlatego o to pytam.
wookieb
Po pierwsze nie każdy, po drugie route-y możesz definiować jak tylko chcesz gdzie m.in ZF oraz Symfony2 taką możliwość stwarza.
Po 3 jeżeli FW tego nie umożliwia to jest do dupy.
Gość
No i właśnie dlatego pytam jak?

Kod
$router['*']?
gac3k
w każdym poważnym frameworku, masz możliwość definiowania własnych reguł na bazie wyrażeń regularnych a te już niestety musisz albo znać albo kombinować metodą prób i błędów. Ten podstawowy routing, oparty na kontrolerze, akcji parametrze, jest właściwie wręcz niezalecany poza developingiem.
Gość
Ok, a jak w takim razie są robione podkategorie? Zakładam że jest takie wyrażenie (a-z0-9) . czyli wszytko leci do kontrolera X. Jednak co w wypadku gdy jest /mac/safari/new/jeszczecosinnego ? Wtedy odczytuje załóżmy "jeszczecośinnego", ale skąd tzn. jak to sprawdzać czy to akurat należy do NEW w SaFARI, a to z kolei do MAC?
by_ikar
Ehh, zorzum jedno że taki router, nie rozbija adresu url i tylko na nim bazuje, a ma podane dodatkowe atrybuty których ty w adresie nie ujrzysz. W symfony jest to fajnie przedstawione, zresztą router symfony bardzo mi przypadł do gustu i napisałem sobie podobny. Zobacz na to:

link: http://www.example.com/articles/finance/20...-breakdown.html

kod dla routera:

Kod
article_by_title:
  url:    articles/:subject/:year/:title.html
  param:  { module: article, action: permalink }


Już rozumiesz na jakiej zasadzie są przekazywane informacje o tym jaki to jest moduł/akcja itp? Podobnie jest w innych routerach, zobacz sobie chociażby horde routes: http://dev.horde.org/routes/integrate.html tam również działa to na podobnej zasadzie.
Gość
by_ikar - tylko chodzi o to iż na apple.com nie ma czegoś takiego jak sztywne odwołanie do module:article, bo niby jak? Załóżmy że w cms ktoś tworzy podstrone "jakaśtam" do głównej strony "coś" czyli link http://asd.pl/cos/jakastam już rozumiesz o co mi chodzi?
thek
Ja niedawno coś takiego robiłem. To w zasadzie nieco inaczej przemyślany routing. Ogólnie przyjąłem założenia że:
1) kategorie i subkategorie nie mają _ w nazwie
2) jedyne dozwolone znaki to a-z 0-9 , _ - gdzie:
a) a-z 0-9 to standardowe konwersje do nice url znaków alfanumerycznych
b) , służy do oddzielenia id w sytuacji gdy jakiś parametr wymaga identyfikatora
c) _ służy do oddzielenia pary klucz-wartość, przy czym wartość jako jedyna może mieć identyfikator oddzielony przecinkiem
d) - zastępuje znaki niedozwolone i puste, przy czym nie może wystąpić więcej niż taki jeden znak obok siebie. Jeśli jest inaczej, skaracam te kilka znaków do jednego -
W efekcie mój "router" przyjmuje przykładowy adres w postaci /kategoria/subkategoria/subkategoria2/parametr_wartosc/parametr2_wartosc-jakas,98/

Jest tak przemyślany, że sprawdza wystepowanie subkategorii i ustawia je w url po kolei oraz sprawdza czy subkategoria występuje w "rodzicu" ją poprzedzającym i jeśli nie, usuwa z url ją oraz te będące jej potomkami. Efekt?
/kategoria/subkategoria1/parametr_wartosc/subkategoria2/parametr2_wartosc-jakas,98/
podlega wstępnej konwersji do
/kategoria/subkategoria1/subkategoria2/parametr_wartosc/parametr2_wartosc-jakas,98/
i teraz zależnie czy subkategorie są dziećmi swoich rodziców (i o ile występują takie w spisie kategorii/subkategorii!) adres może przyjąć kilka wariantów:
/kategoria/subkategoria1/subkategoria2/parametr_wartosc/parametr2_wartosc-jakas,98/
/kategoria/subkategoria1/parametr_wartosc/parametr2_wartosc-jakas,98/
/kategoria/parametr_wartosc/parametr2_wartosc-jakas,98/
/parametr_wartosc/parametr2_wartosc-jakas,98/

Parametry także są sprawdzane i one także mogą być pozostawione lub usunięte. Dodatkowo nad całością czuwa konfig, który ustawia mi kolejność parametrów w url i jeśli przyjda w zmienionej kolejności, ustawia je prawidłowo. Nie wspomniałem także, że pewne parametry, podobnie jak kategorie mogą być zależne i owe zalezności też są sprawdzane. Przykład? Jesli ktoś spróbuje na pałę wpisać miasto, to podczas routowania wyłapuje miasto i dopisuję do adresu województwo tego miasta. W przypadku niejednoznaczności kieruję do strony, która informuje o tym fakcie i każe doprecyzować z jakiego województwa ma to być miasto (poda w jakich województwach to miasto jest i zwyczajnie da link do kliknięcia ;) ). A jeśli nadal nie jest to jednoznaczne, to każe wybrać dodatkowo powiat w obrębie województwa. A by było jeszcze weselej, to może w przypadku błedu zasugerować miasto inne , którego częścią nazwy jest wpisana fraza. Kolejny przykład... wpisując Biała... Znajdzie zarówno miejscowości Bia, ale jeli takowej nie ma to poszuka takie, które zaczynają się od Bia, czyli choćby Białystok czy Biała-Podlaska.

Całość drzewka kategorii i ich zależności zaś dla wydajności, trzymam sobie w cache'u serwera ;) Wtedy idąc od roota, wiem na jakim poziomie ktoś się rypnął i zostaję na ostatnim prawidłowym. Jesli już pierwsza jest walnięta, usuwam wszystkie kategorie i pozostają mi jedynie dodatkowe parametry do zlookania.

EDIT: takie podejście sprawia, że moge mieć teoretycznie nieskończenie zagłębione kategorie, podobnie jak ilość parametrów może być nielimitowana.

EDIT2: zaznaczam, że to jest tylko szkic logiczny, algorytm ogólny, gdyż kodu nie udostępniam. Bazuje on na fw Kohana 3.X i używa klas stworzonych dla niego na potrzeby firmy :) A te nie będą siła rzeczy udostępniane i tym samym kod bez nich nie ruszy.
Zapomniałem dodać, że w takim wypadku jak napisałem kategoria słuzy jako odpowiednik article w przykładzie by_ikar'a inie jest stricte kategorią, ale znakiem rozpoznawczym dla routingu, w jaki route ma się pchać. By ciutkę to ułatwić, podam nieco zmienioną regułkę :)
Kod
Route::set('glowna', 'kategoria(/<slug>)(/<pagination>)', array('slug' => '([a-żA-Ż-,]{1,}[0-9]{0,}/{0,1})+', 'pagination' => '[0-9]+'))

Kategoria służy jedynie tu do określenia, że mamy do czynienia z czymś, co trzeba mieć by mój router rzucił operacje na kawał kodu odpowiedzialny za kategorie i subkategorie. Jeśli go nie będzie to poleci na kolejne reguły w routes. Uzywam tego jako bardziej inteligentnej wyszukiwarki, która tworzy SEO-friendly adresy url z zapytania wysłanego POSTem. Określasz warunki, a ona sobie przetwarza dane, tworzy url i kieruje do niego. Póki co nigdzie to jeszcze nie stoi i jest w fazie developingu, ale uproszczone wersje w kilku serwisach firmy już działają i sprawują się porządnie.
by_ikar
No przecież pokazałem ci przykład że to nie jest sztywne odwołanie do moduł/akcja. Popatrz na to, jak można w symfony taki adres zapisać, wygląda bardzo podobnie, ale odpalę go w 2 różnych modułach:

konfiguracja:
Kod
article_by_id:
  url:    articles/:subject/:year/:id.html
  param:  { module: readArticle, action: read }
  requirements: { id: \d+}

article_by_title:
  url:    articles/:subject/:year/:title.html
  param:  { module: article, action: permalink }


dla @article_by_id powstanie taki link: /articles/finance/2006/265.html
dla @article_by_title powstanie taki link: /articles/finance/2006/activity-breakdown.html

a mogę sobie równie dobrze pozamieniać miejscami te wartości i dla routera to będzie nie istotne w którym miejscu one są. Zauważ że przy każdym adresie odpalam dwa różne moduły i dwie różne akcje. Poprostu na podstawie konfiguracji, router tworzy wyrażenie regularne, które następnie jest dopasowywane do adresu jaki masz w przeglądarce, jeżeli nie zostanie dopasowany, jedzie dalej, tak długo aż nie znajdzie, a jak nie znajdzie rzuca błędem 404. A o to jakie wyrażenia powstaną po podaniu powyższej konfiguracji routerowi:

Kod
@article_by_id: #^/articles/(?P<subject>.+)/(?P<year>.+)/(?P<id>\d+)\.html$#x
@article_by_title: #^/articles/(?P<subject>.+)/(?P<year>.+)/(?P<year>.+)\.html$#x


Linki minimalnie różne, a odpalany jest różny moduł i różna akcja. A przecież parametry możesz umieścić w jakim miejscu tylko chcesz, nie musi być na końcu jak w moim przypadku. Prócz tego że dane są pozyskiwane z adresu, dostajesz też dane z routingu, czyli moduł, akcje itp, bo możesz tam jeszcze jakieś inne parametry zmieścić. Podobnie działa routing do którego dałem ci link. Jakby ci się chciało w ogóle przejrzeć, to byś nie zadawał takich pytań..
Gość
by_ikar - nie zrozumiałeś. W moim przypadku Twój przykład musiałby wyglądać tak:

Kod
article_by_id:
  url:    /:subject/:year/:id
  param:  { module: readArticle, action: read }
  requirements: { id: \d+}


Zakładając że nie wiem jakie linki i co będzie wczytywane:

Kod
article_by_id:
  url:    (a-z0-9).html
  param:  { module: readArticle, action: read }
  requirements: { id: \d+}


I teraz muszę tylko pomyśleć JAK zrobić żeby było odpowiednie wyświetlanie tego co ma być. Dajmy na to artów. Opis już dałem wyżej. thek dobrze napisał o co chodzi i jak to ma działać.
by_ikar
Tak czy inaczej, podałem ci tylko przykład. To co potrzebujesz, w bardziej zaawansowanych systemach routingu jest możliwe, wystarczy tylko odpowiednio zdefiniować routing.

BTW przykłady routingu które podałeś również są możliwe. Z tym że ty nie wiesz jak w ogóle się do tego zabrać ani na jakiej zasadzie to działa, to i podajesz przykład jak w drugim listingu, który jest błędny. Powinno to raczej wyglądać tak:

Kod
article_by_id:
  url:    /:subject.html
  param:  { module: article, action: read }
  requirements: { subject: "[\w\d]+" }
thek
@Zxcy: reguła w stylu (a-z0-9).html jest delikatnie mówiąc chybiona. Czemu? Ponieważ łapie większość url-i strony, stąd coś takiego jak "kategoria" lub jakiekolwiek inne słowo jest w zasadzie konieczne, bo inaczej zmuszasz serwer do pchania niemal wszystko przez jeden moduł. A od tego masz właśnie router, czyli niejako dublowałbyś jego pracę. Tego typu rzeczy właśnie router na podstawie sensownych reguł wykonuje. Dlatego tworzy się zazwyczaj regułę ze słowem, generującym ciąg ładny dla SEO, czyli w przypadku kategorii choćby:
/oferty/marketingowe/inne/wojewodztwo_slaskie/3/ co skieruje router na moduł odpowiedzialny za oferty, które są w kategorii "marketingowe" i subkategorii "inne" a dodatkowo jest to strona 3 tychże z wojewodztwa śląskiego. Takie mniej więcej jest tego rozwinięcie i tak powinno to działać, dlatego by_ikar ma rację. Zauważ, że taka reguła jak jego niemal cały ruch url-i kieruje na modul: article i akcje read. A co z innymi modułami? Jak one miały by być łapane? Pomyślałeś o tym? wink.gif Może być tak, że faktycznie pierwszy człon jest kontrolerem i główną kategorią, ale i tak dubluje on zadania routera, gdyż musi znać całą hierarchię swoich potomków by przez nie sobie przejść i sprawdzić wszystko, co jest IMHO głupotą, bo wymusza niepotrzebne redirecty/forwardy wewnątrz serwisu w razie błędnych url-i. To już powinien załatwiać router bazowo. Z tego powodu Wordpress jest jednym z najgłupszych skryptów masowo używanych. To, że zarżyna serwer gdy ludzie palną w seo-friendly url %postname%.html jest jednym z takich kwiatków, które wołają o jakąś krucjatę smile.gif Zdebuguj sobie co robi Wordpress z bazą w każdym przejściu wtedy to Ci gałki na wierzch wyjdą.
by_ikar
Zajrzałem na apple.com z tymi adresami które podał @Zxcy i w sumie nie wiem, jak dla mnie to jest zwyczajnie jeden i ten sam moduł, tylko w przypadku www.apple.com/macosx/safari/ wyświetla akcję safari (powiedzmy) a w przypadku www.apple.com/macosx/ wyświetla domyślną akcje, czyli index (powiedzmy). I routing do takiego rozwiązania, to już nawet super uniwersalny może wyglądać tak:

Kod
module_index:
    url:    /:module
    param:  { action: index }

default:
    url:    /:module/:action/*


i jest to domyślny routing symfony i pewnie podobnie jest w zendzie, kohanie i wielu innych bardziej zaawansowanych frameworkach wink.gif
-Guciu-
A jak to jest odczytywane dla podkategorii? Jak konstruować takie linki z bazy? Pobierać wszytkie dane np macox > sfari > new ? czy jak?>
thek
No przecież wspomniałem jak można to zrobić. Masz dwa wyjścia. O jednym wspomniałes czyli po odczycie z routera kategorii, sprawdzasz jej istnienie w bazie i ewentualnych dzieci. Ale jest to tak częsta akcja, że powinieneś to odpuścić bo zajedziesz bazę. Dlatego zapisz sobie strukturę kategorii w cache serwera iodwołuj do niego. Znacznie szybsze rozwiązanie. Przykład?
  1. $kategorie = array (
  2. 'macosx' => array(
  3. 'name' => 'Mac OSX',
  4. 'children' => array(
  5. 'safari' => array(
  6. 'name' => 'Safari',
  7. 'children' => array()
  8. )
  9. 'lion' => array(
  10. 'name' => 'Lion version',
  11. 'children' => array()
  12. )
  13. )
  14. ),
  15. );
Czemu taka choćby? Bo łatwo po niej sie "idzie" z powodu rekurencji. Każda nazwa będąca jako kategoria jest kluczem tablicy (czyli nazwą seo-friendly, widoczną z poziomu url). Posiadać ona może kilka własności takich jak pełna nazwa (name) i tablicę dzieci (moja ma znacznie więcej, ale te dwie to raczej minimum). Teraz sprawdzenie wygląda banalnie. Albo idziemy od roota, albo od najdalszego liścia:
isset($kategorie['level-1']['children']['level-2']) i jeśli nie ma to odrzucamy ostatni poziom i children jako błędne. Ja mam dla przykładu to rozwiązane tak, że zaczynam od roota i przy starcie cache się sprawdza czy tę strukturę posiada. Jeśli tak - używa, jeśli nie generuje i wrzuca do cache'u. Jakakolwiek modyfikacja struktury (dodanie, aktualizacja, usunięcie lub przeniesienie węzła czy węzłów) usuwa bieżącą strukturę z cache. Proste, skuteczne, wydajne.
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.