Nietypowy routing dla kontrolera

Pracuje pod Ruby 1.8.7, Rails 3.0.3, MySQL 5.1.49.

Obecnie mam mniej więcej taki kod HTML:

<a class="first selected" href="#">Strona główna</a> <a href="#">Regulamin</a> <a href="#">FAQ</a> <a href="#">Kontakt</a> <a href="#">Do pobrania</a>
Chcę by kontroler przybierał taką postać:
/article/ID/tytuł
Tytuł nie powinien być brany pod uwagę, tak go daje by ładniej linki wyglądały.

Wyjdzie coś takiego:

<a class="first selected" href="#">Strona główna</a> <a href="/article/1/regulamin">Regulamin</a> <a href="/article/2/faq">FAQ</a> <a href="/article/3/kontakt">Kontakt</a> <a href="/article/4/do-pobrania">Do pobrania</a>
Da się coś takiego zrobić?
Z góry dziękuje za odpowiedź.

Z tego powinienes cos wykminic: http://railscasts.com/episodes/117-semi-static-pages :wink:

Zobacz https://github.com/norman/friendly_id
Może lekko nagniesz swoje wymagania :slight_smile:

Ewentualnie polecam rozwiązanie w stylu:
http://www.jroller.com/obie/entry/seo_optimization_of_urls_in
(tutaj jeszcze trzeba zadbać o obsługę polskich znaków)

W 3 w nowym routingu można to zrobić tak:

match 'article/:id/:title' => "articles#show"

(nie weryfikowałem, ale powinno działać). Dostajesz id i title w params, oczywiście title możesz zignorować.
Wszystko pod warunkiem, że kontroler nazywa się “ArticlesController” i posiada akcję “show”.

generalnie możesz w ogóle olać :id i uzyć metody #to_url z gema stringex połączonej z metodą to_param.

Więcej informacji w redame https://github.com/rsl/stringex

[quote=Matthias]<a class="first selected" href="#">Strona główna</a> <a href="/article/1/regulamin">Regulamin</a> <a href="/article/2/faq">FAQ</a> <a href="/article/3/kontakt">Kontakt</a> <a href="/article/4/do-pobrania">Do pobrania</a>
Da się coś takiego zrobić?
Z góry dziękuje za odpowiedź.[/quote]
Jeśli to są strony “statyczne”, zrób tak w routing.rb:

get "regulamin" => "pages#regulamin", :as => :regulamin get "faq" => "pages#faq", :as => :faq get "kontakt" => "pages#kontakt", :as => :kontakt get "do-pobrania" => "pages#do_pobrania", :as => :do_pobrania
Akcji w kontrolerze w ogóle nie musisz definiować (lub mogą być puste).

Jeśli natomiast stoi za tym model, najlepsze rozwiązanie dał Świstak - gem stringex w czysty i kompletny sposób wspiera przyjazne URL-e.

jeśli są to strony statyczne, to w public/ tworzy się stronę .html i odwołuje przez strona.pl/foo.html. A gdy mamy naprawdę mocno obciążony serwis, to wydziela się takie strony gdzieś “wysoko”, żeby taki request wogóle nie dotykał stosu railsowego.

Jeśli plik leży w public to żądanie nie dotyka w ogóle railsów (zakładam, że serwer jest poprawnie skonfigurowany). Poza tym po co tworzyć statyczne pliki w public/ jak można zrobić normalne widoki i dać im keszowanie do statycznych plików? Po co? Jak Ci przyjdzie zmienić coś w layoucie to zmienisz w 1 pliku, usuniesz cache i działa :).

[quote=apohllo]W 3 w nowym routingu można to zrobić tak:

match 'article/:id/:title' => "articles#show"

(nie weryfikowałem, ale powinno działać). Dostajesz id i title w params, oczywiście title możesz zignorować.
Wszystko pod warunkiem, że kontroler nazywa się “ArticlesController” i posiada akcję “show”.[/quote]
Działa. Co prawda jest wymagany title ale mimo wszystko bangla.

Jak będę miał czas to wypróbuje pozostałe metody.

Edycja:

match 'articles/:id/:title' => "articles#show" match 'articles/:id/' => "articles#show"
I po problemie.

[quote=Matthias][quote=apohllo]W 3 w nowym routingu można to zrobić tak:

match 'article/:id/:title' => "articles#show"

(nie weryfikowałem, ale powinno działać). Dostajesz id i title w params, oczywiście title możesz zignorować.
Wszystko pod warunkiem, że kontroler nazywa się “ArticlesController” i posiada akcję “show”.[/quote]
Działa. Co prawda jest wymagany title ale mimo wszystko bangla.[/quote]

  1. Title powinien być wymagany, bo inaczej będziesz miał duplicated content w google.

  2. Jeśli jednak chciałbyś zrobić go opcjonalnym, wystarczy ująć w nawiasy: ‘article/:id(/:title)’.

  3. Nie używaj match. Te “makro” akceptuje każdą metodę HTTP. Tymczasem akcja kontrolera powinna odpowiadać na dokładnie jedną metodę HTTP. Zamień “match” na “get” i ciesz się czystością publicznego interfejsu aplikacji.

To czy title jest wymagany czy nie nie ma żadnego wpływu na SEO. Przy wymaganym tytule możesz mieć równie dobrze /articles/1/hello i /articles/1/world i jeśli pobierasz artykuł po ID to i tak masz powieloną treść.

Lepszym rozwiązaniem tego problemu jest użycie rel=“canonical” (lub ew. przekierowań).

Ma duży wpływ: jeśli na jednej stronie zalinkują /articles/1, a na drugiej /articles/1/dupa, to te ścieżki przez gugla będą traktowane jako dwa różne URLe (czyli o osobnych pagerankach)

A co powiecie na to:

[code]# W routes.rb
resources :articles, :only => :show

w models/article.rb

def to_param
“#{self.id}-#{self.title}”.parameterize
end

Przykład użycia w widoku

zakładając, że article jest instancją modelu Article

<%= link_to article.title, article_path(article) %>

Wygeneruje się ładny url np. “/articles/2-pytania-i-odpowiedzi”[/code]

Nie trzeba trzeba do tego gemów, parameterize zajmie się i znakami diaktrycznymi i białymi.

Można skorzystać z tego, że jeśli w kontrolerze mamy coś w stylu @article = Article.find(params[:id]) to :id może przyjąć wartość “2”, może też przyjąć “2-pytania-i-odpowiedzi”, efekt będzie ten sam. Wtedy można zrezygnować z dodatkowego ukośnika, który jest w tym przypadku mylący, bo sugeruje zejście na poziom niżej.

To tylko konwencja railsów, że ukośnik coś takiego “sugeruje”. Konwencja dla mnie, na przykład, bardzo irytująca.

katafrakt - akurat nie tylko Railsy to “sugerują” sugeruje to też zwykle system plików linuxowy i mnóstwo mnóstwo innych systemów.

RazorJack: stringex ma tylko tą przewagę nad parametrize że radzi sobie na przykład bardzo ładnie z polskimi znakami i znakami typu & @ etc.