Rails: appeler une autre action de contrôleur à partir d'un contrôleur

118

Je dois appeler l'action de création dans le contrôleur A, à partir du contrôleur B.

La raison en est que je dois rediriger différemment lorsque j'appelle du contrôleur B.

Cela peut-il être fait dans les rails?

ddayan
la source
Parlez-vous de l'action POST ou GET? Si GET vous pouvez simplement rediriger vers cette action. Mais la raison en n'est pas claire car vous pouvez rediriger du contrôleur A vers l'URL de votre choix.
Voldy
duplication possible de Appeler un contrôleur d'un autre
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

64

Vous pouvez utiliser une redirection vers cette action:

redirect_to your_controller_action_url

En savoir plus: Guide des rails

Pour simplement rendre la nouvelle action:

redirect_to your_controller_action_url and return
Spyros
la source
5
Je suis désolé mais pouvez-vous dire quelle est la différence entre la première ligne et la deuxième ligne? Je pense d'abord exécutera les lignes de code restantes, puis redirigera. Est-ce exact?
aks
Je pense que le dernier exemple était peut-être une faute de frappe et devrait l'être à la renderplace redirect_to. Que dites-vous, @Spyros?
Magne
1
Salut @spyros, le redirect_tone permet pas d'utiliser post: :methodet cela peut être utile notamment pour rediriger vers une createaction déjà existante d'un autre contrôleur comme @ddayan l'a demandé à la première fois. J'ai une situation similaire où, dans certaines situations, je devrais créer un autre objet. Appeler l'autre createaction peut être DRYer ..
SanjiBukai
53

Pour utiliser un contrôleur à partir d'un autre, procédez comme suit:

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
la source
18
Si vous vouliez que les rappels soient toujours exécutés, controller_you_wantvous le feriezcontroller_you_want.process(:action_you_want)
Constantijn Schepens
3
Merci! Ceci est très utile dans les situations où vous ne souhaitez pas rediriger, vous avez juste besoin d'une solution rapide pour adopter une action d'un contrôleur à un autre. La redirection est vraiment quelque chose de différent. Aussi: render status: :ok, json: JSON.parse(controller.render(:action_you_want).first)semble fonctionner pour renvoyer JSON de l'autre contrôleur
Yourpalal
1
Excellente réponse, exactement ce que je cherchais. Une question - quel format les paramètres de demande devraient-ils prendre ici? Je suppose qu'ils vivent sous controller_you_want.requestmais n'ont pas pu obtenir ce tir en passant une instance de hachage ou de paramètres.
SRack
Je ne sais pas ce que vous demandez à @SRack. Le paramsdevenir disponible controller_you_wanten définissant le requestdans la 3ème ligne. C'est ce que vous demandez?
Sammy Larbi
1
essayé dans les rails 5, pour le rendu il devrait êtrerender html: controller_you_want.process(:action_you_want)
nazar kuliyev
41

La logique que vous présentez n'est pas compatible MVC, donc pas Rails.

  • Un contrôleur rend une vue ou une redirection

  • Une méthode exécute du code

A partir de ces considérations, je vous conseille de créer des méthodes dans votre contrôleur et de les appeler depuis votre action.

Exemple:

 def index
   get_variable
 end

 private

 def get_variable
   @var = Var.all
 end

Cela dit, vous pouvez faire exactement la même chose avec différents contrôleurs et invoquer une méthode à partir du contrôleur A pendant que vous êtes dans le contrôleur B.

Le vocabulaire est extrêmement important c'est pourquoi j'insiste beaucoup.

apnée
la source
Je sais que je ne devrais pas faire cela, mais cela ne fait pas partie de mon application, c'est juste pour tester et évaluer mon application.
ddayan
9
J'adore ... séparez la méthode du rendu, puis appelez la méthode individuelle si nécessaire. Juste une question ... comment peut-on get_variablemaintenant être appelé depuis un autre contrôleur?
FloatingRock
1
@FloatingRock vient de remarquer votre question / commentaire: vous avez plusieurs options: peut être défini dans l'ancêtre commun ou vous pouvez inclure un mixin commun
apneadiving le
J'adore la réponse, je ne suis pas sûr du fragment de phrase de fin
Gerard Simpson
30

Vous pouvez utiliser url_forpour obtenir l'URL d'un contrôleur et une action, puis utiliser redirect_topour accéder à cette URL.

redirect_to url_for(:controller => :controller_name, :action => :action_name)
Austin
la source
1
l'autre ne semblait pas fonctionner pour moi, ça a l'air mieux, mais comment passer des paramètres?
msanjay
3
@msanjay vous pouvez les transmettre comme autres paramètres à url_for. Par exemple, cela redirect_to url_for(:controller => :controller_name, :action => :action_name, :param1 => :val1, :param2 => :val2)aboutira /contorller_name/action_name?param1=val1&param2=val2. Voir la documentation
Ariel Allon
si j'essaye de rediriger vers un contrôleur racine comme "MyOtherController", à partir d'un contrôleur comme "Module :: MyController" .. il se résoudra à appeler "module / MyOtherController" .. une idée comment je peux appeler la racine?
ggez44
12

C'est une mauvaise pratique d'appeler une autre action de contrôleur.

Vous devriez

  1. dupliquez cette action dans votre contrôleur B, ou
  2. enveloppez-le comme une méthode modèle, qui sera partagée avec tous les contrôleurs, ou
  3. vous pouvez étendre cette action dans le contrôleur A.

Mon avis:

  1. La première approche n'est pas DRY mais c'est toujours mieux que d'appeler à une autre action.
  2. La deuxième approche est bonne et flexible.
  3. La troisième approche est ce que je faisais souvent. Je vais donc montrer un petit exemple.

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

Vous pouvez donc envoyer ce redirect_toparamètre d' action , qui peut être le chemin de votre choix.

fl00r
la source
Salut, pour la solution B wrap comme méthode de modèle, comment conclure s'il n'y a pas de modèle du tout? Nous travaillons sur un moteur de recherche et souhaitons appeler des vues de recherche dans le moteur de recherche à partir d'autres moteurs. Il n'existe aucun modèle de données pour l'action de recherche.
user938363
@ user938363 - Peut-être que les deux actions rendent toutes les deux la même vue (même si ces actions sont dans des contrôleurs différents). L'appel "render" lui-même sera dupliqué mais ce n'est en soi qu'une ligne de duplication - pas si mal. Si vous avez beaucoup de logique qui prépare le hachage des paramètres à passer à l'appel de rendu, vous pouvez éviter de le dupliquer en le déplaçant dans son propre fichier (peut-être un modèle dans /modelsou une classe ou un module ordinaire dans /lib). Le seul problème est que si votre contrôleur communique avec la vue via des variables d'instance - vous devrez corriger cette duplication d'une autre manière.
antinome
Que faire si vous disposez d'un UserController qui crée un nouvel utilisateur (enregistrement) et qu'en cas de succès, vous souhaitez invoquer un SessionsController et authentifier l'utilisateur? Dans ce cas, vous invoquez d'une manière ou d'une autre SessionsController. Des pensées à ce sujet?
dipole_moment
Pourquoi est-ce une mauvaise pratique? quel est le problème avec la réponse de Sammy Lambi?
vasilakisfil
7

Peut-être que la logique pourrait être extraite dans un assistant? les helpers sont disponibles pour toutes les classes et ne transfèrent pas le contrôle. Vous pouvez vérifier dedans, peut-être pour le nom du contrôleur, pour voir comment il a été appelé.

Michael Durrant
la source
6

La composition à la rescousse!

Étant donné la raison, plutôt que d'appeler des actions entre les contrôleurs, il faut concevoir des contrôleurs pour séparer les parties partagées et personnalisées du code. Cela aidera à éviter à la fois la duplication de code et la rupture du modèle MVC.

Bien que cela puisse être fait de plusieurs façons, l'utilisation des préoccupations ( composition ) est une bonne pratique.

# 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

J'espère que cela pourra aider.

Oleg Afanasyev
la source
2

Vous pouvez appeler une autre action dans une action comme suit:

redirect_to action: 'nom_action'

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

  def action2
  end
end
CodecPM
la source
-6

Séparez ces fonctions des contrôleurs et placez-les dans un fichier modèle. Incluez ensuite le fichier de modèle dans votre contrôleur.

Michale.Gaocl
la source
Mauvaise suggestion. Va gâcher les responsabilités, pointez pour avoir MVC. Problèmes avec les appels de session / cookie dans le modèle, etc.
Ain Tohvri