我正在创建一个Category模型并使用awesome_nested_set
插件(acts_as_nested_set
的替代品)来处理层次结构。使用awesome_nested_set
,创建对象,然后保存,然后放置在集合中。同样,lft
,rgt
和parent_id
为attr_protected
,因此无法直接写入。{/ p>
当我将节点放入我想要捕获的集合中时,我遇到了两种情况,因此我通知用户(可能还有更多我还没想过):
self.id == self.parent_id
)self.descendants.include? self.parent_id == true
)在这两种情况下,移动都会失败,但awesome_nested_set
只会引发ActiveRecord::ActiveRecordError
例外,其中的消息不像我希望能够提供给用户那样具有描述性。
awesome_nested_set
有许多节点移动方法,所有方法都调用move_to(target, position)
(其中position
是:root
之一,:child
,{{1}或:left
和:right
是所有target
的相关节点,但position
)。该方法触发:root
回调,但没有提供一种方法,我可以看到它在发生之前验证移动。为了验证移动,我需要访问回调未收到的目标和位置。
有没有人知道在before_move
中验证移动的方法(通过一种方法将目标和位置传递给另一个方法的awesome_nested_set
回调),或另一个嵌套的set插件那会让我验证吗?我不想分叉或编写自己的插件。
答案 0 :(得分:3)
以下是我提出的解决方案:
class Category < ActiveRecord::Base
acts_as_nested_set :dependent => :destroy
#=== Nested set methods ===
def save_with_place_in_set(parent_id = nil)
Category.transaction do
return false if !save_without_place_in_set
raise ActiveRecord::Rollback if !validate_move parent_id
place_in_nested_set parent_id
return true
end
return false
end
alias_method_chain :save, :place_in_set
def validate_move(parent_id)
raise ActiveRecord::RecordNotSaved, "record must be saved before moved into the nested set" if new_record?
return true if parent_id.nil?
parent_id = parent_id.to_i
if self.id == parent_id
@error = :cannot_be_child_of_self
elsif !Category.all.map(&:id).include?(parent_id)
@error = :given_parent_is_invalid
elsif descendants.map(&:id).include? parent_id
@error = :cannot_be_child_of_descendant
end
errors.add(:parent_id, @error) if @error
return @error.nil?
end
def place_in_nested_set(parent_id)
if parent_id.nil? || parent_id.blank?
move_to_root
else
move_to_child_of parent_id
end
return true
end
end
现在,在控制器中,我只需要说@category.save(parent_id)
,其中parent_id
是nil
或父级的ID,然后处理验证,节点放置和保存在模型中。