2016-01-19 3 views
0

Я следую книге «Agile Web Development with Rails 4» и выполнил упражнение Playtime, создав кнопку уменьшения, чтобы уменьшить количество товара в виртуальной корзине покупок. Он должен быть удален, если его количество равно одному.Agile Web Development with Rails 4 - Итерация F - Действие по уменьшению -> повторное использование уничтожения

Хотя это непростая задача, я беспокоюсь о дублировании кода, потому что в моем контроллере есть действие destroy, которое удаляет элемент с помощью AJAX. Если я изменю это позже, я тоже не хочу менять его для декрементации. Кроме того, мне нужно скопировать весь AJAX, который я использую для удаления в decrmentation. Я хотел бы использовать этот метод со всеми его AJAX и т. Д., Чтобы обрабатывать случай, когда это последний элемент такого рода в корзине, чтобы удалить его.

Немного как это (псевдокод):

IF last_item_of_that_kind 
    execute destroy_action 
ELSE 
    decrement 
    forward_to ... 
END 

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

LineItemController:

# DELETE /line_items/1 
# DELETE /line_items/1.json 
def destroy 
    @cart = Cart.find(session[:cart_id]) 
    @line_item.destroy 

    respond_to do |format| 
    format.html { redirect_to cart_url(@cart), notice: 'Line item was successfully destroyed.' } 
    format.js { @current_item = @line_item } 
    format.json { head :no_content } 
    end 
end 

# POST /line_items 
# POST /line_items.json 
def decrement 
    @cart = Cart.find(session[:cart_id]) 
    @line_item = LineItem.find_by_id(params[:id]) 

    # did it in the cart-model first, but that does not allow to redirect correctly 
    if @line_item.quantity > 1 
    @line_item.quantity -= 1 
    if @line_item.save 
     respond_to do |format| 
     format.html { redirect_to cart_url(@cart), notice: 'Line item was successfully decreased.' } 
     format.js { @current_item = @line_item } 
     format.json { head :ok } 
     end 
    end 
    else 
    # did not find a way to call the destroy-mehtod with post, to avoid code-duplicates 
    @line_item.destroy 
    respond_to do |format| 
     format.html { redirect_to cart_url(@cart), notice: 'Line item was successfully destroyed.' } 
     format.js { @current_item = @line_item } 
     format.json { head :ok } 
    end 
    end 
end 

Конечно, было бы лучше, чтобы справиться с самой декрементирования в корзину модели, но тогда я должен был бы проверить значения, тоже.

ответ

1

Вы можете справиться с логикой того, следует ли уменьшать или уничтожать в модели, что упростит ваш контроллер.

Обратите внимание на использование меток !, которые обходят проверку. save не работает, если @line_item находится в недействительном состоянии.

class LineItem < ActiveRecord::Base 
    def decrement_or_destroy! 
    quantity > 1 ? decrement!(:quantity) : destroy! 
    end 
end 

Таким образом, в вашем контроллере, ваш код будет:

# dry controller actions further 
before_action :set_cart_line_item, only: [:destroy, :decrement]  

def destroy 
    @line_item.destroy 

    destroy_line_item_response 
end 

def decrement 
    @line_item.decrement_or_destroy! 

    if @line_item.destroyed? 
    destroy_line_item_response 
    else 
    decrement_line_item_response 
    end 
end 

private 
def set_cart_line_item 
    # assume that all line items should be accessed through cart 
    # don't want people posting line_item ids belonging to other carts 
    @cart = Cart.find(session[:cart_id]) 
    @line_item = @cart.line_items.find_by_id(params[:id]) 
end 

def decrement_line_item_response 
    respond_to do |format| 
    format.html { redirect_to cart_url(@cart), notice: 'Line item was successfully decreased.' } 
    format.js { @current_item = @line_item } 
    format.json { head :ok } 
    end 
end 

def destroy_line_item_response 
    respond_to do |format| 
    format.html { redirect_to cart_url(@cart), notice: 'Line item was successfully destroyed.' } 
    format.js do 
     @current_item = @line_item 
     render file: 'line_items/destroy' 
    end 
    format.json { head :ok } 
    end 
end 
+0

Это хорошая практика, чтобы назвать контроллер-методы в модели? Зачем мне нужно, чтобы save-method обходил проверку? Я не вижу случая для этого. Для методов создания необходимо, и в точке декрементации или удаления он должен быть действительным. Спасибо за Ваш ответ. – Klumbe

+0

@Klumbe: '#decrement!' И '#destroy!' - оба метода модели, принадлежащие 'ActiveRecord :: Base', а не контроллер. Если ваш 'LineItem' имеет недопустимые данные (например.цена установлена ​​на '' 100 "' для целочисленного поля вместо '100'), он всегда будет удаляться вместо декрементированного. – fylooi

+0

О, хорошо, но если они принадлежат к модели, это не помогает мне с моей проблемой относительно повторного использования моего контроллера-метода (особенно перенаправления, AJAX и т. Д.), Хотя это полезная информация. Вы хотите сказать, что невозможно удалить недопустимый 'LineItem' без оператора bang? По-моему, оно будет удалено так или иначе (в любом случае «количество <= 1'). Поскольку элемент сохраняется, прежде чем я не вижу, почему валидация не сработает или как цена изменится на неправильный тип, если я не делаю этого явно (чего я не делаю). – Klumbe

0

Я нашел способ в некотором коде, представленном несколькими страницами позже в книге (страница 168, кому интересно). Можно позвонить по телефону LineItem.destroy(@line_item), если необходимо.

В общем: ControllerName.method(parameters).

Я думаю, что это гораздо лучший подход, а затем переписывание всей переадресации.

декремент-метод будет выглядеть, как это сейчас:

# POST /line_items 
# POST /line_items.json 
def decrement 
    @cart = Cart.find(session[:cart_id]) 
    @line_item = LineItem.find_by_id(params[:id]) 

    # did it in the cart-model first, but that does not allow to redirect correctly 
    if @line_item.quantity > 1 
    @line_item.quantity -= 1 
    if @line_item.save 
     respond_to do |format| 
     format.html { redirect_to cart_url(@cart), notice: 'Line item was successfully decreased.' } 
     format.js { @current_item = @line_item } 
     format.json { head :ok } 
     end 
    end 
    else 
    # call the destroy-method and run all associated 
    LineItem.destroy(@line_item) 
    end 
end 

Так что теперь AJAX-часть может быть отделена и код дублирования можно избежать.

Просто для информации: Уменьшение количества должно происходить внутри модели, но мне было легче справляться с этими двумя случаями.

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