前 基礎Ruby on Rails #25 Chapter14 多対多の関連付け
次 終了
名前空間付きのルーティングとコントローラ
名前空間を導入する理由
- 一般会員、管理者で、ユーザー種類ごとに別々のコントローラを用意する。
- Admin::MembersControllerのようなコントローラ作成する。このとき、
Admin
はモジュール名。 - controllersのイメージ
- admin
- top_controller・・・ 管理者用トップページ
- members_controller・・・会員管理ページ
- articles_controller・・・記事管理ページ
- top_controller・・・一般用トップページ
- members_controller・・・会員表示ページ
- articles_controller・・・記事表示ページ
- admin
管理TOPページへのルーティング設定
-
/admin
→top#index
へのルーティングを設定する。
config/routes.rb(一部)
namespace :admin do
root "top#index"
end
- 上記パスに飛ぶリンクを作成する方法は以下の通り
link_to "管理ページ", :admin_root
link_to "管理ページ", [:admin, :root]
Admin::Baseクラス
- 全ての管理者用クラスの親クラス
Admin::Base
を作成する。 - 全てのこのクラスの継承クラスで認証を行うためのメソッド
admin_login_required
を追加する。エラーはForbidden。
app/controllers/admin/base.rb
class Admin::Base < ApplicationController
before_action :admin_login_required
private def admin_login_required
raise Forbidden unless current_member&.administrator?
end
end
Admin::TopController
- Admin::TopControllerと、indexを作成する。
$ bin/rails g controller admin/top index
create app/controllers/admin/top_controller.rb
invoke erb
create app/views/admin/top
create app/views/admin/top/index.html.erb
- 継承元親クラス
ApplicationController
をAdmin::Base
に書き換える。
app/controllers/admin/top_controller.rb
class Admin::TopController < Admin::Base
def index
end
end
- indexのビューを作成する。
app/views/admin/top/index.html.erb
<% @page_title = "管理ページトップ" %>
<ul>
<li><%= link_to "会員管理", "#" %></li>
<li><%= link_to "ニュース記事管理", "#" %></li>
</ul>
- 管理用なのか一目でわかるようにメニューバーを切り替える。
- 一般用のメニューバーである新規ファイル_menubar.html.erbを作成する。
app/views/shared/_menubar.html.erb
<nav class="menubar">
<ul>
<%= menu_link_to "TOP", :root %>
<%= menu_link_to "ニュース", :articles %>
<%= menu_link_to "ブログ", :entries %>
<% if current_member %>
<%= menu_link_to "会員名簿", :members %>
<% if current_member.administrator? %>
<%= menu_link_to "管理ページ", :admin_root %>
<% end %>
<% end %>
</ul>
</nav>
- 管理者用のメニューバーを新規作成する。
app/views/shared/_admin_menubar.html.erb
<nav class="menubar" id="admin-menubar">
<ul>
<%= menu_link_to "管理TOP", :admin_root %>
<%= menu_link_to "会員管理", "#" %>
<%= menu_link_to "ニュース記事管理", "#" %>
<%= menu_link_to "TOP", :root %>
</ul>
</nav>
- コントローラの種類によって、メニューバーを切り替える。
app/views/shared/_header.html.erb(一部)
<%=
if controller.kind_of?(Admin::Base)
render "shared/admin_menubar"
else
render "shared/menubar"
end
%>
- 管理者用のメニューバーの色を変えるために、CSSを追加する。
app/assets/stylesheets/admin.css
nav#admin-menubar {
background-color: #800;
}
- 管理者用のメニューバーは赤く表示された。
- Jiroでアクセスすると403エラーになることを確認。
会員管理ページの作成
ルーティング設定
- MembersControllerではindexとshow以外のアクションを廃止するために、
only: [:index, :show]
を追加する。
config/routes.rb(一部)
resources :members, only: [:index, :show] do
get "search", on: :collection
resources :entries, only: [:index]
end
- Admin::MembersControllerへのルーティングを追加する。
config/routes.rb(一部)
namespace :admin do
root "top#index"
resource :members do # 以下を追加する
get "search", on: :collection
end
end
- 以上の設定変更により、新たなルーティングが次のように設定される。
アクション | パス | HTTPメソッド | パスを返すメソッド |
---|---|---|---|
index | /admin/members | GET | admin_members_path |
show | /admin/members/123 | GET | admin_members_path(member) |
new | /admin/members/new | GET | new_admin_member_path |
edit | /admin/members/123/edit | GET | edit_admin_members_path(member) |
create | /admin/members | POST | admin_members_path |
update | /admin/members/123 | PATCH | admin_members_path(member) |
destroy | /admin/members/123 | DELETE | admin_members_path(member) |
search | /admin/members/search | GET | search_admin_members_path |
- 管理者向けメニューバーのテンプレートに会員管理ページへのリンクを追加する。
app/views/shared/_admin_menubar.html.erb(一部)
<%= menu_link_to "会員管理", :admin_members %>
- 管理トップページのテンプレートにも会員管理ページへのリンクを追加する。
app/views/shared/_admin_menubar.html.erb(一部)
<li><%= link_to "会員管理", :admin_members %></li>
Admin::MembersControllerの作成
- 今までのmembersのcontrollerとviewのファイルを全てadminにコピーする。
- app/controllersディレクトリの下のmembers_controller.rbをコピーして、app/controllers/adminディレクリにコピーする
- app/views/membersディレクトリをまるごとコピーして、app/views/adminディレクトリの下に貼り付ける。
- MembersControllerに名前空間と親クラスを変更して、before_actionを削除する。
- create、update、destroyアクションの中にあるリダイレクト先を名前空間付きのものに変更する。
app/controllers/admin/members_controller.rb(一部)
#変更前
class MembersController < ApplicationController
before_action :login_required
#変更後
class Admin::MembersController < Admin::Base
# create
redirect_to [:admin, @member], notice: "会員を登録しました。"
# update
redirect_to [:admin, @member], notice: "会員を更新しました。"
# destroy
redirect_to :admin_members, notice: "会員を削除しました。"
会員管理ページ用のテンプレート修正
- パスの指定を名前空間付きのものに変更する。
- アクションのシンボルが名前空間を表すシンボルよりも前に来る点に注意すること。
app/views/admin/members/index.html.erb(一部)
<!-- 変更前 -->
<% @page_title = "会員名簿" %>
<!-- 変更後 -->
<% @page_title = "会員管理" %>
<!-- 変更前 -->
<div class="toolbar"><%= link_to "会員の新規登録", :new_member %></div>
<!-- 変更後 -->
<div class="toolbar"><%= link_to "会員の新規登録", :new_admin_member %></div>
<!-- 変更前 -->
<td><%= link_to member.name, member %></td>
<!-- 変更後 -->
<td><%= link_to member.name, [:admin, member] %></td>
<!-- 変更前 -->
<td><%= link_to "編集", [:edit, member] %>|
<%= link_to "削除", member, method: :delete, data:{ confirm: "☺本当に削除しますか❓" } %></td>
<!-- 変更後 -->
<td><%= link_to "編集", [:edit, :admin, member] %>|
<%= link_to "削除", [:admin, member], method: :delete, data:{ confirm: "☺本当に削除しますか❓" } %></td>
app/views/admin/members/show.html.erb(一部)
<!-- 変更前 -->
<div class="toolbar"><%= link_to "編集", [:edit, @member] %></div>
<!-- 変更後 -->
<div class="toolbar"><%= link_to "編集", [:edit, :admin, @member] %></div>
- 一般向けのshowアクションのテンプレートでは「編集」リンク自体を削る。
app/views/members/show.html.erb
<% @page_title = "会員の詳細" %>
<h1><%= @page_title %></h1>
<!-- 削除 <div class="toolbar"><%= link_to "編集", [:edit, @member] %></div> -->
<%= render "body" %>
- adminのnew、editアクションでは、リンクに:adminを追加する。
app/views/admin/members/new.html.erb
<!-- 変更前 -->
<%= form_for @member do |form| %>
<!-- 変更後 -->
<%= form_for [:admin, @member] do |form| %>
- adminのnew、editアクションでは、リンクに:adminを追加する。
app/views/admin/members/edit.html.erb
<!-- 変更前 -->
<div class="toolbar"><%= link_to "会員の詳細に戻る", @member %></div>
<%= form_for @member do |form| %>
<!-- 変更後 -->
<div class="toolbar"><%= link_to "会員の詳細に戻る", [:admin, @member] %></div>
<%= form_for [:admin, @member] do |form| %>
- adminのnew、editアクションでは、リンクに:adminを追加する。
app/views/shared/_member_form.html.erb(一部)
<!-- 変更前 -->
<% if controller.kind_of?(MembersController) %>
<!-- 変更後 -->
<% if controller.kind_of?(Admin::MembersController) %>
- 一覧、編集、削除が動作することを確認した。
MembersControllerの修正
-
一般会員向けのMembersControllerにはindex、show、searchの3つのアクションだけ残す。
- index、show、search以外のアクションを消す。ストロング・パラメータも消す。
- viewからnew、editを削除する。
-
app/views/members/index.html.erb
- 一覧の新規登録
<div class="toolbar"><%= link_to "会員の新規登録", :new_member %></div>
を消す。 - 一覧の操作列を消す。(編集|削除リンクも消す)
- 一覧の新規登録
-
一般向け画面では、閲覧しかできないことを確認した。
ニュース記事管理ページの作成
- ニュース記事も会員と同様に管理画面をAdminに持っていく。
ルーティングの設定
-
resources :articles
に、only: [:index, :show]
を付加する。 -
namespace :admin do
内に、resources :articles
を追加する。
config/routes.rb(一部)
resources :articles, only: [:index, :show] # 追加
#(省略)
namespace :admin do
root "top#index"
resources :members do
get "search", on: :collection
end
resources :articles # 追加
end
end
- 管理者向けメニューバーのテンプレートにニュース記事管理ページへのリンクを追加する。
app/views/shared/_admin_menubar.html.erb(一部)
<%= menu_link_to "ニュース記事管理", :admin_articles %>
- 管理トップページのテンプレートにもニュース記事管理ページへのリンクを追加する。
app/views/admin/top/index.html.erb(一部)
<li><%= link_to "ニュース記事管理", :admin_articles %></li>
Admin::ArticlesControllerの作成
- app/controllersディレクトリの下のarticle_controller.rbをコピーして、app/controller/adminディレクトリの下に貼り付ける。
- app/views/articlesディレクトリをまるごとコピーして、app/views/adminディレクトリの下に貼り付ける。
- クラスの定義に名前空間をつける。
- index、show、create、update、destroyアクションを書き換える
app/controllers/admin/articles_controller.rb(一部)
# 変更前
class ArticlesController < ApplicationController
# 変更後
class Admin::ArticlesController < Admin::Base
# 変更前
def index
@articles = Article.order(released_at: :desc)
@articles = @articles.open_to_the_public unless current_member
unless current_member&.administrator?
@articles = @articles.visible
end
@articles = @articles.page(params[:page]).per(5)
end
# 変更後
def index
@articles = Article.order(released_at: :desc).page(params[:page]).per(5)
end
# 変更前
def show
articles = Article.all
articles = articles.open_to_the_public unless current_member
unless current_member&.administrator?
articles = articles.visible
end
@article = articles.find(params[:id])
end
# 変更後
def show
@article = Article.find(params[:id])
end
# 変更前 create、update、destroy
redirect_to @article, notice: "ニュース記事を登録しました。"
redirect_to @article, notice: "ニュース記事を更新しました。"
redirect_to :articles, notice: "ニュース記事を削除しました。"
# 変更後
redirect_to [:admin, @article], notice: "ニュース記事を登録しました。"
redirect_to [:admin, @article], notice: "ニュース記事を更新しました。"
redirect_to :admin_articles, notice: "ニュース記事を削除しました。"
記事管理用のテンプレート修正
- indexテンプレートの書き換え
app/views/admin/articles/index.html.erb(一部)
<!-- 変更前 -->
<div class="toolbar"><%= link_to "新規作成", :new_article %></div>
<!-- 省略 -->
<td><%= link_to article.title, article %></td>
<!-- 省略 -->
<%= link_to "編集", [:edit, article] %> |
<%= link_to "削除", article, method: :delete, data: { confirm: "本当に削除しますか?" } %>
<!-- 変更後 -->
<div class="toolbar"><%= link_to "新規作成", :new_admin_article %></div>
<!-- 省略 -->
<td><%= link_to article.title, [:admin, article] %></td>
<!-- 省略 -->
<%= link_to "編集", [:edit, :admin, article] %> |
<%= link_to "削除", [:admin, article], method: :delete, data: {confirm: "本当に削除しますか?"} %>
- showテンプレートの書き換え
app/views/admin/articles/show.html.erb(一部)
<!-- 変更前 -->
<div class="toolbar"><%= link_to "編集", [:edit, @article] %></div>
<!-- 変更後 -->
<div class="toolbar"><%= link_to "編集", [:edit, :admin, @article] %></div>
- newテンプレートの書き換え
app/views/admin/articles/new.html.erb(一部)
<!-- 変更前 -->
<%= form_for @article do |form| %>
<!-- 変更後 -->
<%= form_for [:admin, @article] do |form| %>
- editテンプレートの書き換え
app/views/admin/articles/edit.html.erb(一部)
<!-- 変更前 -->
<p><%= link_to "記事の詳細に戻る", @article %></p>
<%= form_for @article do |form| %>
<!-- 変更後 -->
<p><%= link_to "記事の詳細に戻る", [:admin, @article] %></p>
<%= form_for [:admin, @article] do |form| %>
- 管理者向けニュース一覧
ArticlesControllerの修正
- 全ユーザー向けのArticlesControllerにはindexアクションとshowアクションだけを残す。
- app/views/articleのnew,edit,_formの3つのビューを削除する。
app/controllers/articles_controller.rb
# 変更前
class ArticlesController < ApplicationController
before_action :login_required, except: [:index, :show]
def index
@articles = Article.order(released_at: :desc)
@articles = @articles.open_to_the_public unless current_member
unless current_member&.administrator?
@articles = @articles.visible
end
@articles = @articles.page(params[:page]).per(5)
end
def show
articles = Article.all
articles = articles.open_to_the_public unless current_member
unless current_member&.administrator?
articles = articles.visible
end
@article = articles.find(params[:id])
end
end
# 変更後
class ArticlesController < ApplicationController
before_action :login_required, except: [:index, :show]
def index
@articles = Article.visible.order(released_at: :desc)
@articles = @articles.open_to_the_public unless current_member
@articles = @articles.page(params[:page]).per(5)
end
def show
articles = Article.visible
articles = articles.open_to_the_public unless current_member
@article = articles.find(params[:id])
end
end
app/views/articles/index.html.erb
<% @page_title = "ニュース一覧" %>
<h1><%= @page_title %></h1>
<% if @articles.present? %>
<% @articles.each do |article| %>
<h2><%= article.title %></h2>
<p>
<%= truncate(article.body, length: 80) %>
<%= link_to "もっと読む", article %>
</p>
<div class="article-footer">
<%= article.released_at.strftime("%Y/%m/%d %H:%M") %>
</div>
<% end %>
<% else %>
<p>ニュースがありません。</p>
<% end %>
app/views/articles/show.html.erb
<% @page_title = @article.title %>
<h1><%= @article.title %></h1>
<%= simple_format(@article.body) %>
<div class="article-footer">
<%= @article.released_at.strftime("%Y/%m/%d %H:%M") %>
</div>
app/assets/stylesheets/articles.css
div.article-footer {
border-top: 1px #ccf dashed;
padding-top: 4px;
margin-bottom: 8px;
text-align: right;
font-size: 75%;
}
- 一般向けニュース一覧
- 一般向けニュース詳細
まとめ
- 管理ページを作るには名前空間付きのコントローラを作成する。
- 最後の方はなかなか難しかったので、整理して理解できるようにしたい。