Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Nice URLs - kilka pytań
Forum PHP.pl > Forum > PHP
Void
Witam,

w mojej aplikacji MVC staram się właśnie "uprzyjaźnić" przyjazne URLe. Obecnie aplikacja działa na takiej zasadzie, że wszystkie żądania przekierowywane są przez plik .htaccess do pliku bootstrap o nazwie index.php. Nie wnikając w szczegóły - po wywołaniu żądania w pliku index.php inicjalizowany jest m.in. obiekt router, który rozbija adres URL na odpowiednie części i przekazuje je do Front controllera. Przyjęta przeze mnie konwencja urli wygląda tak:
Kod
http://domena.com/module/controller/action/params

Przy czym moduł to odpowiedni katalog na serwerze, zawierający oddzielne pliki kontrolerów i widoków (może być np. moduł publiczny oraz moduł administracyjny), kontroler i akcja to wiadomo, a parametry oddzielone są separatorem, określonym przy tworzeniu obiektu routera.
Czyli przykładowy url wygląda u mnie tak:
Kod
http://domena.com/public/articles/display/id=5

Mimo wszystko wydaje mi się to średnio "friendly", bardziej podoba mi się np. konwencja, jaka jest na stronie głównej php.pl, gdzie po wybraniu artykułu, w URL pojawia się jego przetworzona nazwa (a nie tak jak u mnie identyfikator) oraz kategoria, do której artykuł należy.

W związku mam kilka pytań:

1. W jaki sposób określić na podstawie takiego URLa, jaki artykuł ma być pobrany z bazy. To znaczy: w przypadku ID jest to proste, porównuje się go np. z kluczem głównym bazy. A w tym przypadku nie można tego porównać z tytułem artykułu przechowywanym w bazie, ponieważ wygląda zwykle inaczej (zawiera spacje, polskie znaki, znaki specjalne). Jedynym sposobem wydaje mi się utworzenie dodatkowej kolumny w tabeli z artykułami, przechowującej ten tekstowy identyfikator, ale czy tak to działa?
2. W jaki sposób zrobiony jest taki podział na kategorie (np. domena.com/artykuly/kategoria/nazwa_artykulu) i czy to w ogóle ma sens, skoro i tak nazwa artykułu jest unikatowa?
3. Na niektórych stronach widziałem dodane do tej nazwy artykułu w URLu rozszerzenie .html. Jaki jest cel dodawania tego rozszerzenia? Jest to tylko po to, żeby zmylić użytkownika, czy treść jest generowana dynamicznie, czy statycznie?
4. Czy polskie nazwy np. kontrolerów w adresach (np. domena.com/artykuly/dodaj) to efekt zastosowania jakichś aliasów, czy po prostu tak rzeczywiście nazwane są ich klasy (pytam, bo IMO dziwne wydaje mi się nazwanie pliku ArtykulyController, a w nim metoda DodajAction()).
Fafu
Robisz sobie w bazie takie pole na tytuł bez pl znaków itp. Przy dodawaniu parsujesz tytuł (usuwasz spacje, niedozwolone znaki) i wsadzasz do tego pola. Później tylko WHERE przparsowany_tytul = $jakas_zmienna_z_adresu_url
IMO w tych urlach na php.pl to kategoria jest jedynie upiększaczem.
erix
Cytat
1. W jaki sposób określić na podstawie takiego URLa, jaki artykuł ma być pobrany z bazy. To znaczy: w przypadku ID jest to proste, porównuje się go np. z kluczem głównym bazy. A w tym przypadku nie można tego porównać z tytułem artykułu przechowywanym w bazie, ponieważ wygląda zwykle inaczej (zawiera spacje, polskie znaki, znaki specjalne). Jedynym sposobem wydaje mi się utworzenie dodatkowej kolumny w tabeli z artykułami, przechowującej ten tekstowy identyfikator, ale czy tak to działa?

Osobne pole w tabeli (ja używam ident), tytuł utworzony przez iconv + wyłuskiwanie paru znaków (http://eriz.pcinside.pl/weblog/mod_rewrite...ac-208.html#t81). Choć lepiej jest stosować konwencję mieszaną, np. identyfikator-123 - przy większej ilości tekstów indeksy numeryczne są szybciej przetwarzane aniżeli tekstowe. Wtedy adres żądania rozbijasz po myślniku albo przekazujesz jako osobny parametr, do wyboru, do koloru. ;]

Cytat
2. W jaki sposób zrobiony jest taki podział na kategorie (np. domena.com/artykuly/kategoria/nazwa_artykulu) i czy to w ogóle ma sens, skoro i tak nazwa artykułu jest unikatowa?

Teoretycznie tak. Jednak łatwiej jest zachować porządek, jeśli chodzi o np. przechodzenie do spisu danej kategorii (część użytkowników - jeśli nie znajdzie nawigacji - zrobi to ręcznie); nie wiem, jak zachowują się pod tym względem wyszukiwarki; podejrzewam, że niektóre również wędrują po pseudokatalogach.

Cytat
3. Na niektórych stronach widziałem dodane do tej nazwy artykułu w URLu rozszerzenie .html. Jaki jest cel dodawania tego rozszerzenia? Jest to tylko po to, żeby zmylić użytkownika, czy treść jest generowana dynamicznie, czy statycznie?

W SEO panuje teza, że pliki statyczne są lepiej promowane w wynikach. Google się do tego ustosunkowało: http://googlewebmastercentral.blogspot.com...tatic-urls.html. Ale znając życie, prawda jest 50/50, poza tym - wyszukiwarki, to nie tylko Google. ;]

Cytat
4. Czy polskie nazwy np. kontrolerów w adresach (np. domena.com/artykuly/dodaj) to efekt zastosowania jakichś aliasów, czy po prostu tak rzeczywiście nazwane są ich klasy (pytam, bo IMO dziwne wydaje mi się nazwanie pliku ArtykulyController, a w nim metoda DodajAction()).

Zapewne jest to poprzez routing. U siebie korzystam po prostu z polskich nazw; to jedyny wyjątek, gdzie takie postępowanie dopuszczam. Po co dodatkowe mapowanie artykuly=>articles, dla mnie to tylko zbędny proces, który z czystym sumieniem można pominąć. winksmiley.jpg
Void
Dzięki za odpowiedzi, takich informacji mi brakowało smile.gif

Cytat(erix @ 26.09.2009, 12:27:08 ) *
Osobne pole w tabeli (ja używam ident), tytuł utworzony przez iconv + wyłuskiwanie paru znaków (http://eriz.pcinside.pl/weblog/mod_rewrite...ac-208.html#t81). Choć lepiej jest stosować konwencję mieszaną, np. identyfikator-123 - przy większej ilości tekstów indeksy numeryczne są szybciej przetwarzane aniżeli tekstowe. Wtedy adres żądania rozbijasz po myślniku albo przekazujesz jako osobny parametr, do wyboru, do koloru. ;]

To też ciekawe rozwiązanie, na pewno w przyszłości wykorzystam, jednak na obecnej stronie, którą tworzę nie ma wielu artykułów, dlatego chciałem właśnie zastosować konwencję z tytułem zamiast identyfikatora numerycznego. BTW. właśnie wcześniej szukając jakichś informacji o przyjaznych URLach, trafiłem na ten wpis na Twoim blogu i przyznam, że zainspirował mnie do zagłębienia się w temat smile.gif


Mam jeszcze jedno pytanie. Bo zastanawiam się teraz, jak trochę skrócić obecnie używane w mojej aplikacji URLe, nie tracąc przy tym zalet rozszerzalności aplikacji. To znaczy: mógłbym np. zastosować adres typu domena.com/artykuly/nazwa_artykulu.html, ale wtedy musiałbym już wszędzie pomijać zarówno nazwę modułu, jak i nazwę akcji (bo w tym przykładzie adresu co podałem jest tylko nazwa kontrolera i parametr - nazwa artykułu).

Miałem taki pomysł, żeby dopuścić trzy rodzaje zapisów URLi, które przetworzy aplikacja:
Kod
http://domena.com/moduł/kontroler/akcja/parametry <- domyślny URL ze wszystkimi podanymi elementami
http://domena.com/moduł/kontroler/parametry <- brak podanej akcji, wywoływana zostaje akcja domyślna z podanymi parametrami
http://domena.com/kontroler/parametry <- brak nazwy modułu i akcji, wywoływany kontroler z domyślnego modułu oraz domyślna akcja

Tylko prawdopodobnie znacznie obniżyłoby to wydajność. Bo przecież trzeba przy sprawdzić, czy pierwszy element adresu jest modułem (tzn. odpowiednim katalogiem na serwerze) - jeśli tak, to sprawdzić czy drugi parametr jest kontrolerem (tzn. czy istnieje klasa o tej nazwie), dalej trzeba by dołączyć tą klasę i sprawdzić czy kolejny element URLa jest akcją (tzn. czy w klasie kontrolera istnieje metoda o tej nazwie), itd. Jak widać byłoby mnóstwo warunków, poza tym nawet nie wiem, czy należałoby je wszystkie sprawdzać w Routerze czy we Front Controllerze? Bo żeby np. sprawdzić istnienie metody w klasie, trzeba dołączyć plik tej klasy, a to już jest zadanie front controllera tongue.gif Co o tym sądzicie?
erix
Cytat
Tylko prawdopodobnie znacznie obniżyłoby to wydajność. Bo przecież trzeba przy sprawdzić, czy pierwszy element adresu jest modułem (tzn. odpowiednim katalogiem na serwerze)

Można ostatecznie pójść na kompromis i zrobić tabelę istniejących modułów/kontrolerów - wtedy zwykłe in_array. winksmiley.jpg

Cytat
Jak widać byłoby mnóstwo warunków, poza tym nawet nie wiem, czy należałoby je wszystkie sprawdzać w Routerze czy we Front Controllerze? Bo żeby np. sprawdzić istnienie metody w klasie, trzeba dołączyć plik tej klasy, a to już jest zadanie front controllera Co o tym sądzicie?

Raczej front-controller, choć mogę się mylić, bo nie stosuję tylu pośredników po drodze. tongue.gif
Void
Cytat(erix @ 26.09.2009, 16:36:42 ) *
Można ostatecznie pójść na kompromis i zrobić tabelę istniejących modułów/kontrolerów - wtedy zwykłe in_array. winksmiley.jpg


Myślałem wcześniej o czymś podobnym, z tym że nie planowałem tego zrobić na zasadzie tablicy, a bardziej mapy kontrolerów i modułów przechowywanej w pliku INI (takiej, jaką robi się np. przy autoloaderze).

Na razie postanowiłem w ogóle spróbować wcielić w życie te "inteligentne" rozpoznawanie adresów smile.gif Zrobiłem tak, że w pliku konfiguracyjnym aplikacji (plik ini) przechowuję dane o modułach i poszczególnych akcjach kontrolerów.
Wygląda to tak:
Kod
modules=admin,public
[controllers]
kontroler1=domyslnaAkcja,akcja1,akcja2
kontroler2=akcja1
kontroler3=akcja1,akcja2


A oto jaki piękny warunek analizuje te adresy:
  1. // Pobranie danych konfiguracyjnych.
  2. $config = $this->_context->getConfig();
  3.  
  4. // Pobranie listy modułów, kontrolerów i akcji z pliku konfiguracyjnego.
  5. $modulesList = explode(',', $config->modules);
  6. $controllersList = array_keys($config->getSection('controllers'));
  7. $actionsList = $config->getSection('controllers');
  8.  
  9. // Rozbicie adresu URL na poszczególne elementy.
  10. $elements = explode('/', $this->_url);
  11.  
  12. if (isset($elements[0]) && $elements[0] != '') {
  13. // 0. element adresu istnieje
  14. if (in_array($elements[0], $modulesList)) {
  15. // 0. element adresu i jest poprwną nazwą modułu.
  16. $this->_module = $elements[0];
  17.  
  18. if (isset($elements[1]) && $elements[1] != '') {
  19. // 1. element adresu istnieje
  20. if (in_array($elements[1], $controllersList)) {
  21. // 1. element adresu istnieje i jest poprawną nazwą kontrolera
  22. $this->_controller = $elements[1];
  23.  
  24. if (isset($elements[2]) && $elements[2] != '') {
  25. // 2. element adresu istnieje
  26. if (in_array($elements[2], $actionsList)) {
  27. // 2. element adresu istnieje i jest poprawną nazwą akcji
  28. // np. domena.com/module/controller/action
  29. $this->_action = $elements[2];
  30.  
  31. if (isset($elements[3])) {
  32. $this->_params = explode($paramSeparator, $elements[3]);
  33. }
  34. } else {
  35. // 2. element adresu istnieje, ale nie jest nazwą akcji.
  36. // np. domena.com/module/controller/params
  37. // Przyjmujemy, że element 2. to parametry akcji.
  38. // Wywołujemy akcję domyślną dla kontrolera.
  39. $this->_action = $config->default_action;
  40. $this->_params = explode($paramSeparator, $elements[2]);
  41. }
  42. } else {
  43. // 2. element adresu nie istnieje, ale elementy
  44. // 0. i 1. są poprawnymi nazwami modułu i kontrolera.
  45. // np. domena.com/module/controller
  46. // Wywołujemy więc domyślna akcję
  47. // dla tego kontrolera.
  48. $this->_action = $config->default_action;
  49. }
  50. } else {
  51. // 1. element adresu istnieje, ale nie jest nazwą kontrolera,
  52. // np. domena.com/module/undefined
  53. // Brak przewidzianej akcji dla tej sytuacji.
  54. }
  55. } else {
  56. // 1. element adresu nie istnieje, ale 0. element jest poprawną
  57. // nazwą modułu.
  58. // np. domena.com/module
  59. $this->_controller = $config->default_controller;
  60. $this->_action = $config->default_action;
  61. }
  62. } else {
  63. // 0. element adresu istnieje, ale nie jest nazwą modułu.
  64.  
  65. if (in_array($elements[0], $controllersList)) {
  66. // 0. element adresu jest nazwą kontrolera.
  67. // np. domena.com/controller
  68. $this->_module = $config->default_module;
  69. $this->_controller = $elements[0];
  70.  
  71. if (isset($elements[1])) {
  72. // 1. element adresu istnieje
  73. if (in_array($elements[1], $actionsList)) {
  74. // 1. element adresu jest nazwą akcji.
  75. // np. domena.com/controller/action
  76. $this->_action = $elements[1];
  77. } else {
  78. // 1. element adresu istnieje, ale nie jest nazwą akcji.
  79. // Przyjmujemy, że są to parametry
  80. // np. domena.com/controller/params
  81. $this->_action = $config->default_action;
  82. $this->_params = explode($paramSeparator, $elements[1]);
  83. }
  84. } else {
  85. // 1. element adresu nie istnieje
  86. $this->_action = $config->default_action
  87. }
  88. }
  89.  
  90. } else {
  91. // 0. element adresu nie istnieje.
  92. $this->_module = $config->default_module;
  93. $this->_controller = $config->default_controller;
  94. $this->_action = $config->default_action;
  95. }


Nie wiem, jak to się ma do wydajności, ale wygląda groźnie tongue.gif

Nie mogę na razie sprawdzić jak to działa, bo moja aplikacja jest w trakcie małej refaktoryzacji i kod jest na tyle rozgrzebany, że się nie uruchomi smile.gif Ale na oko ten warunek sprawdza wszystkie założone przeze mnie możliwości i jest raczej poprawny (musiałem pomagać sobie komentarzami, żeby się nie zgubić:P) Niestety podejrzewam jednak, że spadek wydajności będzie na tyle zauważalny, że będę musiał wrócić do stałej konwencji URLi winksmiley.jpg
Pilsener
Ja robię tak, że rozbijam adres na dwie zmienne:
- jedna to lokalizacja domena/kategoria/podkategoria
- druga - słowa kluczowe + parametry

Czyli przykładowy adres mam:
strona.pl/kategoria/podkategoria/45-strona,o,grach;23;6-2.html

A po przepisaniu:
strona.pl/index.php?kat=kategoria/podkategoria&page=45-strona,o,grach;23;6-2.html

I teraz każdą zmienną analizuję kodem PHP podobnym do Twojego, by wyłuskać tablicę danych adresowych. Na podstawie unikalnej nazwy url (definiowanej przez użytkownika jako nazwa kategorii widoczna w adresie url) kategorii/podkategorii pobieram ich id oraz wszystkie niezbędne informacje: zainstalowane dla kategorii moduły, szablony, style itp. Natomiast rdzeniem zmiennej "page" są słowa kluczowe (oddzielone przecinkiem), potem średnikiem lecą podstawowe parametry, natomiast myślnik oddziela parametry dodatkowe (początek stringa) oraz określa numer strony (koniec stringa).

Może coś z tego co napisałem Ci pomoże. Warto przemyśleć na początku budowę adresu, bo od tego dużo zależy, potem można nie dać rady z obróbką winksmiley.jpg
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.