2011-01-10 6 views
6

Używam modelu metody instancji t_param wygenerować adres URL SEO styluto_param warunkowy metoda

def to_param 
    url 
end 

ten sposób mogę wygenerować linki do modelu z path_to_model (model) i kwerendy model z Model.find_by_url (URL). To działa dobrze do tej pory.

Moje pytanie: Mam RESTFUL admin tras dla backendu. Czy mogę w jakiś sposób sprawić, by metoda to_param reagowała na trasę, przez którą jest wywoływana? Ponieważ chcę utworzyć łącza w zapleczu za pomocą parametru ID, a nie parametru adresu URL. Albo jakie jest właściwe podejście?

Odpowiedz

2

Miałem ten sam problem. Model naprawdę nie może/nie powinien wiedzieć nic o kontrolerach. Zadaniem kontrolera jest ustalenie, o jaki zasób jest żądany, oraz wywołanie modelu, aby uzyskać do niego dostęp. Jeśli Twój AdminController potrzebuje standardowego identyfikatora numerycznego, nie będziesz chciał dotknąć to_param.

Okazuje się, że rozwiązanie jest dość proste i używam go w produkcji.

Zakładając, że masz nazwy na swoich kontrolerach, będziesz miał MyModelController i Admin :: MyModelController, wraz z ich pomocnikami. W Admin, wykonaj czynności w standardowy sposób. W MyModelController wykonaj następujące czynności:

W swoich działaniach na zasoby zapoznaj się z params[:id], ale traktuj je jak permalink (URL).Na przykład

def get 
    @my_model = MyModel.find_by_url(params[:id]) 
    ... 
end 

W swojej MyModelHelper

def my_model_path my_model 
    super my_model.url 
end 

def my_model_url my_model 
    super my_model.url 
end 

Dla mnie to było to był najlepszy sposób na „nie” walczyć szyn i dostać to, czego potrzebowałem zrobić.

Może być bardziej sprytny sposób na przesłonięcie nazwanych tras. Myślę też, że nie można używać tej metody z użyciem helper :all, chyba że przed wygenerowaniem ścieżek/adresów URL sprawdzisz, czy jesteś administratorem, czy nie.

0

Oto jeden ze sposobów, aby to zrobić ...

class BlogPost 
    def to_param 
    url 
    end 

    # BlogPost.find_by_param("14") => looks for ID 14 
    # BlogPost.find_by_param("foobar") => looks for url "foobar" 
    # BlogPost.find_by_param("14-foobar") => looks for ID 14 (ignores the "-foobar" part) 
    def self.find_by_param(param) 
    if param.to_s =~ /^[0-9]+/ 
     find_by_id param.to_i 
    else 
     find_by_url param.to_s 
    end 
    end 
end 
+0

samo tutaj, nie chciałbym identyfikator w URL ponieważ chcę ściśle oddzielić wewnętrzne elementy modelu od reprezentacji zewnętrznej poprzez adres URL .. możliwe .. –

+0

To nie spowoduje wstawienia identyfikatora w adresie URL. Wszystko, co robi, to daje kolejną metodę klasy, BlogPost.find_by_param, której używasz zamiast BlogPost.find_by_url, i działa bez względu na to, czy nadasz jej identyfikator czy adres URL. –

2

Czy jesteś całkowicie przeciwny posiadające identyfikator w url? Jeśli nie, to co robiłem w przeszłości (które również realizuje cel 'SEO Friendly' adresów URL jest taki:

class Person 
    def to_param 
    "#{id}-#{name.parameterize}" 
    end 
end 

Zatem zamiast:

http://www.example.com/users/1

masz

http://www.example.com/users/1-jerry-seinfeld

Ponieważ metoda String::to_i zatrzyma raz napotka niecałkowitą charakter (czyli "1".to_i i "1-jerry-seinfeld".to_i zarówno powrócić 1), oznacza to, można również zrobić:

person = Person.find(params[:id]) 

bez konieczności zastąpić żadnym z dystansu (z dodatkową korzyścią pracy na frontend i backend Administrator).

+0

To dobra praca, ale tak naprawdę nie zgadzam się na identyfikator w adresie URL, ponieważ chcę ściśle oddzielić wewnętrzne elementy modelu od reprezentacji zewnętrznej poprzez adres URL. Nadal szukam rozwiązania, w którym mogę metoda to_param reaguje na kontekst, jeśli to możliwe .. –

2

Użyj zmiennej modelu klasy @@to_param i metody klasy do jej sterowania.

Na przykład w modelu

class ProductCategory < ActiveRecord::Base 
    @@to_param = true 

    attr_accessible :description, :name, :parent_id 
    has_ancestry 
    has_many :products 
    validates :slug, :uniqueness => true, :presence => true 

    before_validation :generate_slug 

    #Trigger fo 'to_param' method 
    def self.to_param_trigger(test) 
    if (test) 
     @@to_param = true 
    else 
     @@to_param = false 
    end 
    end 

    def to_param 
    if @@to_param 
     slug 
    else 
     id 
    end 
    end 

    def generate_slug 
    self.slug ||= self.ancestors.map {|item| item.name.parameterize}.push(self.name.parameterize).join('/') 
    end 
end 

W sterowniku:

class Backend::ProductCategoriesController < Backend::BaseController 
    def index 
    # Disable 'to_param' 
    ProductCategory.to_param_trigger false 
    @categories = ProductCategory 
     .select([:id, :name, :slug]) 
     .select("COALESCE(ancestry, '0') as anctr") 
     .order :anctr, :name 
    end 

    def new 
    end 

    def edit 
    end 
end 

before_filter i innych sztuczek w pomoc =)

+0

Jesteś pewien, że to nie zepsuje tego? Jeśli ustawiasz zmienną klasy, wartość będzie istnieć także dla wszystkich klas, więc jeśli uruchomisz to na produkcji, po włączeniu parametru to_param_filter wszystkie żądania będą miały tę samą wartość niezależnie od przestrzeni nazw –

+0

. pytania do przypadków wielowątkowych, ale w moim dość prostym przypadku to rozwiązanie jest odpowiednie – unixs