В качестве примера взята эта форма:
<%= form_with model: @product do |f| %>
<!-- ... --!>
<% end %>
Файл маршрутов:
get "/products", to: "products#index"
get "/products/new", to: "products#new"
post "/products", to: "products#create"
get "/products/:id", to: "products#show", as: "product"
Откуда он знает, что ему нужно вызвать действие create контроллера продуктов?
На чем основано решение?
form_with
угадывает маршрут по модели. Если модель является экземпляром Product
и это new_record?
, то это будет POST /products
. Если это запись persisted?
, то она будет PATCH /products/{:id}
(см. Помощники форм в официальных руководствах Rails).
Это соответствует соглашениям Ruby on Rails и стандартному способу определения маршрутов с помощью rescources :products
(см. Маршрутизация). Это создаст следующие маршруты:
GET /products => ProductsController#index
GET /products/new => ProductsController#new
POST /products => ProductsController#create
GET /products/:id => ProductsController#show
GET /products/:id/edit => ProductsController#edit
PATCH/PUT /products/:id => ProductsController#update
DELETE /products/:id => ProductsController#destroy
Обратите внимание, что вы не используете помощник resources
для генерации маршрутов, когда from_with
работает только тогда, когда вы определяете те же маршруты, что и Rails. В противном случае нам нужно будет передать дополнительный url:
хелперу формы.
Я предлагаю прочитать о FormHelper#form_with в документации API Rails и взглянуть на реализацию этого метода.
Точно! Но важно, что это работает только тогда, когда вы следуете соглашениям об именах Rails.
@spickermann одно небольшое уточнение: с этого аспекта важны только вспомогательные методы. Пока в вашем приложении есть вспомогательный метод products_path
/product_path
, помощники по маршрутизации будут совершенно счастливы, даже если они вернут ерунду.
form_with
просто использует полиморфные помощники маршрутизации , чтобы найти вспомогательный метод маршрутизации, основанный на соглашении о конфигурации, и вызывает его динамически, чтобы получить путь для атрибута формы action
.
Вспомогательные методы маршрутизации генерируются путем вызова макросов маршрутизации в вашем файле config/routes.rb
и доступны в представлении и контроллерах. Когда вы вызываете resources :products
, чтобы объявить обычные маршруты, он определяет вспомогательные методы products_path
и product_path
(и многие другие).
Это не имеет ничего общего с контроллером, и вы действительно можете прекрасно создать форму, используя только модель и ожидаемые вспомогательные методы.
Контроллер назначения на самом деле имеет значение только после того, как пользователь отправит форму, и маршрутизатор Rails попытается сопоставить входящий HTTP-запрос с комбинацией контроллера и действия.
Помощники полиморфной маршрутизации могут угадать, как называются помощники, с помощью API ActiveModel::Naming. Это просто предполагает, что маршруты, таблицы и т. д. могут быть получены из имени класса модели. Это огромная часть «магии» Rails, которая позволяет вашим моделям взаимодействовать с остальной частью фреймворка.
irb(main):006:0> class Product; include ActiveModel::Naming; end
=> Product
irb(main):007:0> Product.model_name
=>
#<ActiveModel::Name:0x00005570c2876c60
@collection = "products",
@element = "product",
@human = "Product",
@i18n_key=:product,
@klass=Product,
@name = "Product",
@param_key = "product",
@plural = "products",
@route_key = "products",
@singular = "product",
@singular_route_key = "product",
@uncountable=false>
polymorphic_url
сначала выясняет, генерирует ли он путь к коллекции или путь к членам, вызывая new_record?
в модели.
Если запись является новой записью или самим классом, она вызывает model_name.route_key
и в конечном итоге вызывает хелпер пути к коллекции products_path
.
Если модель сохраняется, она вызывает model_name.singular_route_key
и передает model.to_param
в качестве аргумента, что приводит к вызову помощника пути к члену product_path(product.to_param)
. to_param
по умолчанию использует product.id
в качестве идентификатора записи.
form_with
также использует persisted?
, чтобы определить, следует ли использовать HTTP-метод POST или PATCH для создания или обновления.
Это объясняет, почему вы можете использовать один и тот же фрагмент для создания/создания, а также для редактирования/обновления. Большой! Большое спасибо.