Przyjazne adresy w Ruby on Rails
Posted by Piotr Sarnacki Sat, 12 Jan 2008 10:47:00 GMT
W dzisiejszych czasach, kiedy SEO dla niektórych jest ważniejsze od treści przyjazne adresy można zobaczyć na większości stron. Dlaczego nie wrzucić ich do naszej aplikacji? Nawet jeżeli komuś nie zależy na SEO, przyjazne adresy są… przyjazne! Użytkownik dostający linka http://foo.com/article/kolejny-artykul-o-naszej-klasie będzie miał szansę zastanowić się sekundę i nie kliknąć (nie to żebym miał coś do naszej klasy, ale boję się ostatnio lodówkę otworzyć, jeszcze szynka zacznie nawijać kogo dodała do znajomych). Same korzyści!
Dodanie przyjaznych adresów jest w aplikacji railsowej bardzo proste jeżeli trzymamy się kilku zasad. Jak to działa? ActiveRecord::Base, po którym dziedziczą udostępnia metodę to_param, która jest wykorzystywana przy generowaniu adresów.
W praktyce wygląda to tak, że jeżeli napiszemy:
link_to @article.name, articles_path(@article)
#albo
link_to @article.name, :controller => "articles", :action => "show", :id => @article
wywołana zostanie metoda to_param i @article zostanie zamieniony na jego id. Można to wykorzysta do naszych niecnych celów.
Można nadpisać to_param
def to_param
"#{id}-#{name.gsub(/[^a-z0-9]+/i, '-')}"
end
Od teraz zamiast id będzie generował się string zawierający name, na przykład: “11-tytuł-artykułu”. Hamerykanie mają mniejszy problem, bo nie mają znaków diakrytycznych i mogą to tak zostawić. gsub zamieni wszystkie znaki nie wchodzące w skład alfabetu na myślnik. U nas jest gorzej, bo nie chcemy mieć adresu: “11-tytu-artyku-u”. I weź teraz zgaduj gdzie dorzucić ogonki. Z pomocą przychodzi Obie Fernandez, który napisał najfajniejszą jaką do tej pory widziałem metodą zamieniającą znaki diakrytyczne na odpowiadające im litery alfabetu Wrzuciłem na serwer wersję z polskimi znakami diakrytycznymi. Taki plik wystarczy wrzucić do folderu initializers dla Railsów 2.0.x, albo do katalogu lib dla 1.2.x (w tym wypadku trzeba też w environment.rb dodać linijkę require ‘ascii’). Zrobiłem jeszcze jedną modyfikację – to_url_format powinien moim zdaniem w miejsce spacji i innych znaków wrzucać myślniki. Lepiej wygląda “tytul-artykulu” niż “tytulartykulu”.
Dzięki temu String udostępnia 2 nowe metody: `to_url_format` i `to_ascii`:
"zażółć gęślą jaźń".to_ascii #=> "zazolc gesla jazn"
"zażółć gęślą jaźń".to_url_format #=> "zazolc-gesla-jazn"
Teraz wystarczy zmodyfikować lekko to_param:
def to_param
"#{id}-#{name.to_url_format}"
end
Dzięki temu urle będą miały upragnioną formę: “12-tytul-artykulu”.
Tylko po co to id na początku? Dzięki temu obędzie się bez żadnych zmian w kontrolerach. Taki string zostanie przed wrzuceniem do bazy automatycznie skonwertowany na liczbę całkowitą. Czyli kolejna rzecz, która “po prostu działa” automagicznie. Jeżeli id będzie potrzebne w jakimś innym miejscu, w którym nie nastąpi konwersja można to zrobić ręcznie: params[:id].to_i, bo:
"11-jakis-napis".to_i #=> 11
Jaki jest minus takiej metody? Adresy mają w sobie id i tylko na podstawie tego id jest pobierany artykuł, więc czy wpiszemy /articles/11-tytul-artykulu, czy /articles/11-tralalala pobierze się ten sam artykuł.
Żeby temu zapobiec najlepiej zrobić dodatkową kolumnę, na przykład “permalink” i wpisywać do niej przekształcony adres. Np. article.permalink = article.name.to_url_format . I zamiast po id szukać po kolumnie permalink: Article.find_by_permalink(params[:id]). Oczywiście z metody to_param też trzeba usunąć id :)
Nie każdemu to drugie podejście będzie potrzebne. Dla mnie wersja z id jest o tyle lepsza, że jak nawet ktoś nie skopiuje całego adresu, albo pomyli się przy wpisywaniu (tak, czasami zdarza się, że ktoś dyktuje jakiś adres), to jest duża szansa, że dotrze na dobrą stronę. Poza tym istnieje wtedy możliwość wklejenia adresu bez dalszej części, która może być dość długa – może nie trzeba będzie korzystać z serwisów typu tinyurl.


Wydaje mi sie ze lepiej zamiast tworzyc za kazdym razem taki link, dodac do modelu symbol:string i przechowywac go na stale w bazie. W przypadku gdy chcemy zmienic algorytm tworzenia symbolu wystarczy dodac akcje “rebuild symbols” oraz callback przy save aby za kazdym razem gdy zmienia sie name byl zmieniany tez symbol.
Tak, zgadzam się :) Ja daję jeszcze z reguły możliwość edycji tej kolumny ręcznie. Czasami przy długich tytułach można wpisać jakiś skrócony adres, czasami z innych powodów ktoś może chcieć go zmienić. Nie pomyślałem nawet o tym, żeby o tym napisać, jak będę miał chwilę czasu to dodam parę słów.
Użyłem Twojego ascii.rb w aplikacji RoR 1.2.6 i się sypnęło przy uruchamianiu Mongrela (chciał aktywować Rails 2.0.2, chociaż już aktywował Rails 1.2.6).
Usunąłem “require ‘unicode’” i o dziwo, chyba działa i przestało błądzić.
Być może problem w tym, że na maszynie do pisania mam railsy i stare i nowe?
Zapomniałem zaznaczyć, że wsparcie dla Unicode’u w tej postaci jest dostępne od wersji 2.0 – szczerze mówiąc nie sprawdzałem jak to działa bez tego pliku, ale jeżeli chodzi spoko, to się cieszę :)