如何找到两个物体之间的距离?

时间:2019-02-16 08:23:15

标签: ruby-on-rails ruby geocoding

我正在使用地理编码。这个想法是我们的​​合作伙伴可以发布带有地址的产品。当他们这样做时,它将获取经度和纬度。现在,当我们的客户去购买该产品时,他们必须输入送货地址以告诉我们该产品在哪里送货。但是,如果他们的送货地址不在产品的20英里之内,则不允许他们送货。

我收到一条错误消息,提示此“ nil:NilClass的未定义方法'latitude'”

就像我说的那样,当用户尝试订购产品时,product.longitude,product.latitude已经设置。

不确定是否是因为order.delivery_address(lat,long)尚未提交到数据库中,并且它正在尝试检查距离。这是我的下面的代码

所以我的问题是如何找到产品地址和订单地址之间的距离,并且如果两者之间的距离超过20英里,我想向用户显示警报消息。

 def create
        product = Product.find(params[:product_id])
        if current_user == product.user
            flash[:alert] = "You cannot purchase your own property"

        elsif current_user.stripe_id.blank? || current_user.phone_number.blank?
            flash[:alert] = " Please update your payment method and verify phone number please"
            return redirect_to payment_method_path
        elsif Geocoder::Calculations.distance_between([product.latitude, product.longitude], [@order.latitude, @order.longitude]) < 20
            flash[:alert] = "The delivery address you provided is outside the delivery zone. Please choose a different product."        
        else
            quantity = order_params[:quantity].to_i 

        @order = current_user.orders.build(order_params)
        @order.product = product
        @order.price = product.price
        @order.total = product.price * quantity + product.delivery_price

        # @order.save

        if @order.Waiting!
            if product.Request?
                flash[:notice] = "Request sent successfully... Sit back and relax while our licensed dispensary fulfil your order :)"
            else
                @order.Approved!
                flash[:notice] = "Your order is being filled and it will delivered shortly:)"
            end
        else
            flash[:alert] = "Our licensed dispensary cannot fulfil your order at this time :( "
        end

        end
        redirect_to product
    end

2 个答案:

答案 0 :(得分:2)

您在以下行中设置了@order

@order = current_user.orders.build(order_params)

但是您甚至在设置longitude变量之前,尝试在其之上调用其latitude@order方法。为了简单地解决此问题,您可以将该行上移,甚至可以将其放在create方法的开头,因为它不依赖于product或类似的东西:

def create
  @order = current_user.orders.build(order_params)
  # ...
end

尽管您的代码中存在许多问题,例如以大写字母开头的方法名称(您可以做到这一点,但是您不应该这样做,这违反了惯例)或代码的整体复杂性方法。

答案 1 :(得分:1)

您应该将业务逻辑移动到它所属的模型中。

因此,让我们首先为产品距离创建一个验证:

go build

然后将总计的计算移入模型:

class Order < ApplicationRecord

  validates :product_is_within_range, 
    if: -> { product.present? } # prevents nil errors

  # our custom validation method
  def product_is_within_range
    errors.add(:base, "The delivery address you provided is outside the delivery zone. Please choose a different product.") if product_distance < 20
  end

  def product_distance
    Geocoder::Calculations.distance_between(product.coordinates, self.coordinates)
  end
end

但是您仍然必须处理控制器非常坏的事实。例如:

class Order < ApplicationRecord
  before_validation :calculate_total!, if: ->{ product && total.nil? }

  def calculate_total!
    self.total = product.price * self.quantity + product.delivery_price
  end
end

应使该方法保释。您实际上也没有保存记录。我会重新开始。针对不同的可能情况(无效参数,有效参数,用户是所有者等)编写失败的测试,然后编写控制器代码。确保测试每个代码分支。