Railsルーティングヘルパー(mymodel_path(model))をモデルで使用できますか?


368

ThingというRailsモデルがあるとします。Thingには、オプションでインターネット上のどこかにあるURLに設定できるurl属性があります。ビューコードでは、次のことを行うロジックが必要です。

<% if thing.url.blank? %>
<%= link_to('Text', thing_path(thing)) %>
<% else %>
<%= link_to('Text', thing.url) %>
<% end %>

ビューのこの条件付きロジックは醜いです。もちろん、ビューを次のように変更するヘルパー関数を作成することもできます。

<%= thing_link('Text', thing) %>

これで冗長性の問題は解決しますが、モデル自体に機能を持たせたいと思います。この場合、ビューコードは次のようになります。

<%= link_to('Text', thing.link) %>

これには明らかに、モデルにリンクメソッドが必要です。これが含まれる必要があるものです:

def link
  (self.url.blank?) ? thing_path(self) : self.url
end

問題は、thing_path()はモデルコード内の未定義のメソッドです。いくつかのヘルパーメソッドをモデルに「引き込む」ことが可能だと思いますが、どうやって?そしてルーティングがアプリのコントローラーとビューレイヤーでのみ機能するという本当の理由はありますか?モデルコードがURLを処理する必要がある場合(外部システムとの統合など)がたくさんあると思います。


ユースケースは次のようになります:アフターセーブでgoo.glから短縮URLを生成するには
lulalala

2
ビューロジックを追加する場合は、モデルをプレゼンターでラップする必要があります。これにより、MVCレイヤーが分離されたままになります。Draper(github.com/jcasimir/draper)を参照してください。
クリス

回答:


698

Rails 3、4、および5では、以下を使用できます。

Rails.application.routes.url_helpers

例えば

Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")

14
Ypuはこれを任意のモジュールに含めることができ、その後、そこにあるURLヘルパーにアクセスできます。Thx
Viacheslav Molokov

41
あなたのコピーから自分で保存し:host、どこでもオプションをし、環境設定ファイルで一度に設定します。Rails.application.routes.default_url_options[:host] = 'localhost:3000'
アンドリュー

5
include Rails.application.routes.url_helpersRails 4.1で私のために働く
1

1
パスだけが必要な場合は、ホストパラメータを渡さずに、オプションとして:only_path => trueを使用できます。
trueinViso 2014年

1
これは良い選択肢であるように思わ:hawkins.io/2012/03/...
Renars Sirotins

182

これを自分で行う方法に関する答えを見つけました。モデルコードの中に、次のコードを入力します。

Rails <= 2の場合:

include ActionController::UrlWriter

Rails 3の場合:

include Rails.application.routes.url_helpers

これは魔法のようthing_path(self)に、現在のものother_model_path(self.association_to_other_model)のURLを返すか、他のURLを返します。


27
上記は非推奨であるため、更新のみです。:これは、現在あるinclude Rails.application.routes.url_helpers
yalestar

2
これを試すと、NoMethodError(#<ActionView :: Base:0x007fe8c0eecbd0>の未定義メソッド `optimize_routes_generation? ')が発生します
moger777

118

また、すべてのメソッドを含めるよりも次のアプローチの方がきれいな場合もあります。

class Thing
  delegate :url_helpers, to: 'Rails.application.routes' 

  def url
    url_helpers.thing_path(self)
  end
end

7
これに関するドキュメントは見つけにくいように思われたので、ActiveSupportでのデリゲートの使用に関する適切なリンクを次に示します。simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate
tlbrack

2
私が手undefined local variable or method 'url_helpers' for Event:Class:( ...エラー
オーギュRiedinger

わかりますundefined method url_helpers。どうする?
asiniy 2015

@asiniy、クラス宣言の後に記述されたurl_helpersにデリゲートオプションを使用しましたか?
Krzysztof Witczak 2016

機能しませんがclass、答えに示されているように、独自のを作成した場合にのみ機能する可能性があります。Modelクラスがすでに拡張している場合、< ApplicationRecordこれは機能しませんか?
skplunkerin

13

モデル内のメソッドは厳密にデータを処理するため、ビューに表示される内容に関連するロジックはヘルパーメソッドに委任する必要があります。

これがあなたができることです:

# In the helper...

def link_to_thing(text, thing)
  (thing.url?) ? link_to(text, thing_path(thing)) : link_to(text, thing.url)
end

# In the view...

<%= link_to_thing("text", @thing) %>

1
この場合のヘルパーメソッドの好ましくない点:link_to_thing()を見るとき、それが事物固有のヘルパーであるか、アプリケーション全体のヘルパーであるかを判断する必要があります(簡単にどちらかになる可能性があります)。ソースの2つのファイルをチェックすることを検討する必要があります。thing.linkは、ソースファイルについては何も問題を残しません。
アーロンロングウェル

また、(おそらくURLのCSVファイルをエクスポートするために)Rakeタスクでこの機能を使用する必要がある場合はどうなりますか?その場合も、モデルに直接アクセスする方がはるかに優れています。
アーロンロングウェル

しかし、違いは、link_toはActionViewヘルパーであるため、モデルで使用できないことです。したがって、これは機能しません。モデルの一部の属性のデフォルトをハックすることができるので、それが設定されていない場合、デフォルトは何かに設定されますが、それはあなた次第です。
Josh Delsman、2008

12
WebサービスAPIの成長に伴い、多くの場合、モデルは外部リソースに独自のデータを接続し、コールバックURLを提供する必要があります。たとえば、写真オブジェクトはそれ自体をSocialmodに投稿する必要があり、モデレートが実行されるとその写真のURLにコールバックします。after_create()フックはSocialmodに投稿する意味がありますが、どのようにしてコールバックURLを知るのですか?著者自身の答えは理にかなっていると思います。
JasonSmith

3
あなたが厳密に技術的な意味で正しい一方で、神聖なデザインパターンやあなたが持っているものに違反しないようにするために、人々がそこにあるすべてのヤクを剃る必要なしに作業する必要があるだけの場合があります、おそらく彼らはURLを取得して実際に保存する必要がありますデータベースでそれを行うと、モデルが物事をやり取りするのではなく、その方法を知っているだけで便利です。時にはルールが破られる可能性があります。
nitecoder、2012年


1

そのようなロジックをモデルから除外する傾向がある方法があるかもしれませんが。私はそれをビューに入れるべきではないことに同意します(細く保ちます)が、モデルがURLをデータの一部としてコントローラーに返す場合を除いて、ルーティングに関するものはコントローラーにあるはずです。


4
Re:「モデルがデータの一部としてURLを返さない限り」。これがまさにここで行われていることです...モデルはこのデータを「所有」します。これは、オンサイトまたはオフサイトのURLへのリンクです。場合によっては、URLはRailsルーティングから生成されます。他の場合では、URLはユーザー指定のデータです。
アーロンロングウェル

0

(編集:以前のバブルを忘れて...)

わかりました。モデルまたは他のURLに移動する場合があります...しかし、これはモデルに属しているとは思いません。ビュー(またはモデル)のほうが適切に聞こえます。

ルートについて、私が知る限り、ルートは直接ビューにではなく、コントローラーのアクション(通常は「魔法のように」ビューを使用)に対するものです。コントローラはすべてのリクエストを処理し、ビューは結果を表示し、モデルはデータを処理してビューまたはコントローラに提供する必要があります。モデルへのルートについて多くの人が話しているのを聞いたことがありますが(私はほとんどそれを離れようとしています)、ルートはコントローラーに行きます。もちろん、多くのコントローラーは1つのモデルのコントローラーであり、しばしば呼び出されます<modelname>sController(たとえば、「UsersController」はモデル「User」のコントローラーです)。

ビューで厄介な量のロジックを記述していることに気付いた場合は、ロジックをより適切な場所に移動してみてください。リクエストと内部通信ロジックはおそらくコントローラーに属し、データ関連ロジックはモデルに配置できます(ただし、リンクタグなどを含む表示ロジックは含まれません)。純粋に表示関連のロジックはヘルパーに配置されます。


イメージモデルがあるとします。画像に外部URLが関連付けられている場合は、そのURLをポイントします。それ以外の場合は、画像の表示ページをポイントして画像をアップロードしますか?ただのアイデア。
Josh Delsman、2008

このあまり一般的ではない例についてはどうですか:link_to "フルサイズの画像"、image.linkモデルのリンクメソッドは、オンサイトURL(image_path)にリンクするか、ユーザーが提供した場合はFlickr URLにリンクします。画像に.urlが設定されました。
アーロンロングウェル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.