1

Player.rb

class Player < ApplicationRecord 
belongs_to :user 
has_many :player_games, dependent: :destroy, inverse_of: :player 
has_many :games, :through => :player_games 

validates :firstname, presence: true, length: { minimum: 3, maximum: 88 } 
validates :lastname, presence: true, length: { minimum: 3, maximum: 88 } 
validates :user_id, presence: true 

accepts_nested_attributes_for :player_games, reject_if: :reject_posts, allow_destroy: true 

def reject_posts(attributes) 
    attributes['game_id'].to_i == 0 
    attributes['score'].blank? 
    attributes['time'].blank? 
end 

def initialized_player_games # this is the key method 
    [].tap do |o| 
     Game.all.each do |game| 
      if g = player_games.find { |g| g.game_id == game.id } 
       o << g.tap { |g| g.enable ||= true } 
      else 
       o << PlayerGame.new(game: game) 
      end 
     end 
    end 
end 
end 

players_controller.rb

class PlayersController < ApplicationController 
before_action :set_player, only: [:edit, :update, :show, :destroy] 
before_action :require_user, except: [:index, :show] 
before_action :require_same_user, only: [:edit, :update, :destroy] 

before_filter :process_player_games_attrs, only: [:create, :update] 

def process_player_games_attrs 
    params[:player][:player_games_attributes].values.each do |game_attr| 
    game_attr[:_destroy] = true if game_attr[:enable] != '1' 
    end 
end 

.......Рельсы has_many: через, кокон

private 
    # Use callbacks to share common setup or constraints between actions. 
    def set_player 
    @player = Player.find(params[:id]) 
    end 

    # Never trust parameters from the scary internet, only allow the white list through. 
    def player_params 
    params.require(:player).permit(:id, :firstname, :lastname, player_games_attributes: [:id, :game_id, :score, :time, :enable, :_destroy]) 
    end 

    def require_same_user 
    if current_user != @player.user and !current_user.admin? 
     flash[:danger] = "You can edit or delete only your own player" 
     redirect_to root_path 
    end 
    end 

end 

_form из 'ИГРОК'

<%= form_for(@player, :html => {class: "az-form", role: "form"}) do |player_form| %> 

    <%= player_form.label :firstname, class: "az-form__label" %> <br/> 
    <%= player_form.text_field :firstname, class: "az-form__input", placeholder: "Firstname of player", autofocus: true %> 

    <%= player_form.label :lastname, class: "az-form__label" %> </br> 
    <%= player_form.text_field :lastname, class: "az-form__input", placeholder: "Lastname of player" %> 

    <%= player_form.fields_for :player_games, @player.initialized_player_games do |builder| %> 

    <% @game = builder.object.game %> 

    <%= render 'result_fields', f: builder %> 

    <div class="links"> 
     <%= link_to_add_association 'add result', player_form, :player_games, :partial => 'players/result_fields' %> 
    </div> 

    <hr> 

    <% end %> 

    <div class="text-center"> 
    <%= button_tag(type: "submit", class: "az-form__submit") do %> 
     <%= player_form.object.new_record? ? "Create player" : "Update player" %> 
    <% end %> 
    </div> 

<% end %> 

_result_fields.html.erb

<div class="nested-fields"> 

<%= f.hidden_field :game_id, :value => @game.id%> 

<div class="row"> 
    <div class="col-md-12"> 
    <label class="az-form__label az-form__label--unable js-az-form__checkbox" data-check="<%= @game.id %>"> 
     <%= f.check_box :enable %> 
     <%= @game.title %> 
    </label> 
    </div> 
</div> 

<div class="row"> 
    <div class="col-md-6"> 
    <%= f.label :score, 
        class: "az-form__label", :data => {:check => @game.id } %> </br> 
    <%= f.number_field :score, step: :any, :data => {:check => @game.id }, 
          class: "az-form__input az-form__input--disabled", 
          placeholder: "Score for '#{@game.title}'", disabled: true %> 
     </div> 
     <div class="col-md-6"> 
    <%= f.label :time, 
        class: "az-form__label", :data => {:check => @game.id } %> </br> 
    <%= f.number_field :time, step: :any, :data => {:check => @game.id }, 
          class: "az-form__input az-form__input--disabled", 
          placeholder: "Time for '#{@game.title}'", disabled: true %> 
    </div> 
</div> 

<div class="row"> 
    <div class="col-md-12"> 
    <%= link_to_remove_association "remove result", f %> 
    </div> 
    </div> 

</div> 

Вопрос:

Когда редактировать «игрок» имеют доступ только один результат за игру, не может изменить других, конфликт с методом «initialized_player_games», но если я удалить этот метод из формы работает хорошо, но не может создать другую игру, если не создавать в новом действии, как я могу правильно изменить этот метод?

ответ

0

Если я правильно понял, вы хотите добавить запись для всех игр (которая работает), но добавление нового Result не делает. Проблема в том, что @game, который вероятен либо неопределенным, либо установленным для последней игры.

Мне также не нравится подход (дающий fields_for определенный набор). Вместо этого я немного адаптирую подход. Вместо использования initialized_player_games я бы использовал метод, который вызывается в контроллере, например. add_default_player_games, что-то вроде

def add_default_player_games 
    Game.all.each do |game| 
    if g = player_games.find { |g| g.game_id == game.id } 
     g.enable ||= true 
    else 
     player_games.build(game: game) 
    end 
    end 
end 

эффективно добавлять новые экземпляры в коллекцию, не сохраняя их.

Так в контроллере можно было бы написать

@player = Player.new 
@player.add_default_player_games 

или edit

@player = Player.find(params[:id]) 
@player.add_default_player_games 

Ваше мнение будет тогда просто перебрать player_games

<%= player_form.fields_for :player_games do |builder| %> 
    <%= render 'result_fields', f: builder %> 
<% end %> 

И затем, если вы будете использовать simple-form было бы очень легко выбрать игру, если бы не y et, и нет необходимости в уродливом @game.

Так что-то подобное в _result_fields (в Haml, потому что я ленивый машинистка)

.nested-fields 
    - game_id = f.object.game_id 
    - if game_id.present? 
    = f.hidden_field :game_id 
    - else 
    = f.collection_select :game_id, Game.all, :id, :name 
    ... 

Короче: если есть game_id, не позволяют изменить его (было бы полезно, чтобы показать название игры или что-то в этом роде), но если вы не используете выпадающий список, выберите игру. И остальное остается неизменным (используйте только game_id вместо @game.id).

+0

Ваш метод работает отлично, теперь я пытаюсь перестроить форму с помощью 'sample_form', кстати у меня есть ошибка на 'f.collection_select: game_id, Game.all.map {| g | [g.name, g.id]} '->' Неверное количество аргументов ', поэтому я удалил этот оператор if, и он работает сейчас –

+0

Я написал этот код в качестве примера, не тестировал его. Я исправил определение 'collection_select'. Если вам вообще не нужно устанавливать игру, вам не нужен выбор, мне казалось, что это действительно важно, чтобы выбрать игру, для которой нужно добавить счет. Но это был всего лишь пример (и вы могли бы использовать любой из альтернативных способов создания опции выпадающего списка). – nathanvda

Смежные вопросы