Railsy: wywołują inną akcję kontrolera z kontrolera

118

Muszę wywołać akcję tworzenia w kontrolerze A, z kontrolera B.

Powodem jest to, że muszę przekierować inaczej, gdy dzwonię z kontrolera B.

Czy można to zrobić w Railsach?

ddayan
źródło
Mówisz o akcji POST lub GET? Jeśli GET możesz po prostu przekierować do tej akcji. Ale przyczyna tego nie jest całkiem jasna, ponieważ możesz przekierować ze sterownika A na dowolny adres URL.
Voldy
możliwy duplikat wywołania kontrolera od innego
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

64

Możesz użyć przekierowania do tej akcji:

redirect_to your_controller_action_url

Więcej na: Przewodnik po Railsach

Aby po prostu wyrenderować nową akcję:

redirect_to your_controller_action_url and return
Spyros
źródło
5
Przepraszam, ale czy możesz powiedzieć, jaka jest różnica między pierwszą linią a drugą linią? Myślę, że najpierw wykona pozostałe wiersze kodu, a następnie przekieruje. Czy to jest poprawne?
aks
Myślę, że może ostatni przykład był literówką i powinien być renderzamiast redirect_to. Co powiesz, @Spyros?
Magne
1
Cześć @spyros, redirect_tonie pozwala na użycie post: :methodi może to być przydatne szczególnie w przypadku przekierowania do już istniejącej createakcji innego kontrolera, o co @ddayan zapytał za pierwszym razem. Mam podobną sytuację, w której w jakiejś sytuacji powinienem stworzyć kolejny obiekt. Wywołanie drugiej createakcji może być DRYer ..
SanjiBukai
53

Aby użyć jednego kontrolera od innego, wykonaj następujące czynności:

def action_that_calls_one_from_another_controller
  controller_you_want = ControllerYouWant.new
  controller_you_want.request = request
  controller_you_want.response = response
  controller_you_want.action_you_want
end
Sammy Larbi
źródło
18
Gdybyś chciał, aby callback nadal był wykonywany controller_you_want, zrobiłbyś tocontroller_you_want.process(:action_you_want)
Constantijn Schepens
3
Dzięki! Jest to bardzo przydatne w sytuacjach, w których nie chcesz przekierowywać, potrzebujesz tylko szybkiego rozwiązania, aby przenieść akcję z jednego kontrolera na inny. Przekierowanie to naprawdę coś innego. Ponadto: render status: :ok, json: JSON.parse(controller.render(:action_you_want).first)wydaje się działać, aby zwrócić JSON z innego kontrolera
Yourpalal
1
Świetna odpowiedź, właśnie to, czego szukałem. Jedno pytanie - jaki format żądania parametrów musiałyby tutaj przyjąć? Zakładam, że żyją pod, controller_you_want.requestale nie udało się uzyskać tego odpalenia, przekazując instancję skrótu lub parametrów.
SRack
Nie jestem pewien, o co pytasz @SRack. Stają paramssię dostępne controller_you_want, ustawiając requestw trzeciej linii. Czy o to pytasz?
Sammy Larbi
1
wypróbowany w szynach 5, do renderowania powinien byćrender html: controller_you_want.process(:action_you_want)
nazar kuliyev
41

Logika, którą prezentujesz, nie jest zgodna z MVC, a następnie z Railsami.

  • Kontroler renderuje widok lub przekierowuje

  • Metoda wykonuje kod

Na podstawie tych rozważań radzę utworzyć metody w kontrolerze i wywołać je z akcji.

Przykład:

 def index
   get_variable
 end

 private

 def get_variable
   @var = Var.all
 end

To powiedziawszy, możesz zrobić dokładnie to samo za pomocą różnych kontrolerów i przywołać metodę z kontrolera A, gdy jesteś w kontrolerze B.

Słownictwo jest niezwykle ważne, dlatego bardzo nalegam.

bezdechu
źródło
Wiem, że nie powinienem tego robić, ale to nie jest część mojej aplikacji, tylko do testowania i oceny mojej aplikacji.
ddayan
9
Uwielbiam to… oddziel metodę od renderowania, a następnie w razie potrzeby wywołaj metodę indywidualną. Tylko jedno pytanie… jak można get_variableteraz wywołać z innego kontrolera?
FloatingRock
1
@FloatingRock właśnie zauważył twoje pytanie / komentarz: masz kilka opcji: możesz zdefiniować ich wspólnego przodka lub możesz dołączyć wspólne połączenie
bezdech
Uwielbiam odpowiedź, niepewny co do końcowego fragmentu zdania
Gerard Simpson
30

Możesz użyć, url_foraby uzyskać adres URL kontrolera i akcji, a następnie użyć, redirect_toaby przejść do tego adresu URL.

redirect_to url_for(:controller => :controller_name, :action => :action_name)
austin
źródło
1
drugi zdawał się nie działać dla mnie, wygląda lepiej, ale jak przekazujemy parametry?
msanjay,
3
@msanjay, możesz przekazać je jako inne parametry do url_for. Na przykład redirect_to url_for(:controller => :controller_name, :action => :action_name, :param1 => :val1, :param2 => :val2)spowoduje /contorller_name/action_name?param1=val1&param2=val2. Zobacz dokumentację
Ariel Allon
jeśli spróbuję przekierować do kontrolera głównego, takiego jak „MyOtherController”, z kontrolera takiego jak „Module :: MyController”… spowoduje to wywołanie „module / MyOtherController” .. jakiś pomysł, jak mogę wywołać root?
ggez44
12

Wywoływanie innej akcji kontrolera jest złą praktyką.

Powinieneś

  1. zduplikuj tę akcję w kontrolerze B lub
  2. opakuj go jako metodę modelową, która zostanie udostępniona wszystkim kontrolerom lub
  3. możesz rozszerzyć tę akcję w kontrolerze A.

Moja opinia:

  1. Pierwsze podejście nie jest SUCHE, ale nadal jest lepsze niż wezwanie do innego działania.
  2. Drugie podejście jest dobre i elastyczne.
  3. Trzecie podejście jest tym, co często robiłem. Więc pokażę mały przykład.

    def create
      @my_obj = MyModel.new(params[:my_model])
      if @my_obj.save
        redirect_to params[:redirect_to] || some_default_path
       end
    end

Możesz więc wysłać do tego redirect_toparametru akcji , którym może być dowolna ścieżka.

fl00r
źródło
Cześć, w przypadku zawijania rozwiązania B jako metody modelowej, jak zakończyć, jeśli w ogóle nie ma modelu? Pracujemy nad wyszukiwarką i chcielibyśmy wywołać widoki wyszukiwania w wyszukiwarce z innych wyszukiwarek. W ogóle nie ma modelu danych dla akcji wyszukiwania.
user938363
@ user938363 - Może obie akcje renderują ten sam widok (nawet jeśli te akcje znajdują się w różnych kontrolerach). Samo wywołanie „render” zostanie zduplikowane, ale samo w sobie jest to tylko jedna linia duplikacji - nie jest tak źle. Jeśli masz dużo logiki, która przygotowuje hash parametrów do przekazania do wywołania renderowania, możesz uniknąć powielania tego, przenosząc go do własnego pliku (być może modelu /modelslub zwykłej klasy lub modułu w /lib). Jedynym problemem jest to, że kontroler komunikuje się z widokiem za pośrednictwem zmiennych instancji - musisz naprawić tę duplikację w inny sposób.
antinome,
Co się stanie, jeśli masz kontroler użytkownika, który tworzy nowego użytkownika (rejestrację), a po pomyślnym zakończeniu chcesz wywołać kontroler sesji i uwierzytelnić użytkownika? W tym przypadku w taki czy inny sposób wywołujesz SessionsController. Jakieś przemyślenia na ten temat?
dipole_moment
Dlaczego jest to zła praktyka? jaki jest problem z odpowiedzią Sammy Lambi?
vasilakisfil
7

Może logikę można by wydobyć z pomocnika? pomocnicy są dostępni dla wszystkich klas i nie przekazują kontroli. Możesz sprawdzić w nim, być może nazwę kontrolera, aby zobaczyć, jak został wywołany.

Michael Durrant
źródło
6

Kompozycja na ratunek!

Biorąc pod uwagę przyczynę, zamiast wywoływać akcje między kontrolerami, należy zaprojektować kontrolery, aby oddzielić wspólne i niestandardowe części kodu. Pomoże to uniknąć zarówno duplikowania kodu, jak i łamania wzorca MVC.

Chociaż można to zrobić na wiele sposobów, dobrą praktyką jest stosowanie obaw ( składu ).

# controllers/a_controller.rb
class AController < ApplicationController
  include Createable

  private def redirect_url
    'one/url'
  end
end

# controllers/b_controller.rb
class BController < ApplicationController
  include Createable

  private def redirect_url
    'another/url'
  end
end

# controllers/concerns/createable.rb
module Createable
  def create
    do_usefull_things
    redirect_to redirect_url
  end
end

Mam nadzieję, że to pomoże.

Oleg Afanasyev
źródło
2

Możesz wywołać inną akcję wewnątrz akcji w następujący sposób:

redirect_to action: „action_name”

class MyController < ApplicationController
  def action1
   redirect_to action: 'action2'
  end

  def action2
  end
end
CodecPM
źródło
-6

Oddziel te funkcje od kontrolerów i umieść je w pliku modelu. Następnie dołącz plik modelu do kontrolera.

Michale.Gaocl
źródło
Zła sugestia. Zepsuje obowiązki, wskaż, że masz MVC. Problemy z wywołaniami sesji / plików cookie w modelu itp.
Ain Tohvri