Atualize a latitude e longitude do geocódigo sempre que o endereço é atualizado
-
26-09-2019 - |
Pergunta
Eu tenho isso no meu shop.rb
:
def geocode_address
if !address_geo.blank?
geo=Geokit::Geocoders::MultiGeocoder.geocode(address_geo)
errors.add(:address, "Could not Geocode address") if !geo.success
self.lat, self.lng = geo.lat,geo.lng if geo.success
end
end
# Checks whether this object has been geocoded or not. Returns the truth
def geocoded?
lat? && lng?
end
E no meu shops_controller.rb
:
def update
@shop = Shop.find(params[:id])
if @shop.update_attributes(params[:shop])
flash[:notice] = "Successfully saved."
redirect_to shop_path(@shop, :type => @shop.shop_type)
else
render :action => :edit
end
end
Agora, quando o usuário cria pela primeira vez a entrada, o endereço é geocodificado, com latitude e longitude salvas no banco de dados.
Mas quando o usuário atualiza o endereço, a latitude e a longitude não serão mais geocodificadas e, portanto, ainda usando a latitude e a longitude antigas, que é o primeiro salvamento.
Como escrevo para que os Rails sejam re-acoderos toda vez que uma entrada é atualizada?
Não posso depender apenas do endereço, porque há um bug no Geokit que, quando tento mostrar vários mapas com base no endereço, apenas o último é mostrado.
Estou usando Geokit, Gmaps, Google Maps ...
Obrigado.
Solução
Eu coloquei isso no meu modelo:
before_validation_on_update :geocode_address
Outras dicas
Se o usuário alterar o endereço deles, você não pode lidar com isso da mesma maneira que um novo endereço? Você basicamente tem dois novos endereços que você só precisa para vincular o endereço recém -criado à conta do usuário e tudo deve funcionar.
A nova sintaxe para executar a validação antes de uma ação específica é:
before_validation :geocode_address, on: :update
ou se você tem mais de uma ação,
before_validation :geocode_address, on: %i[create update]
Isso garantirá que, antes da validação e salvamento no banco de dados, seja feito, seu método (geocode_address
) corre primeiro.
Melhor usar gema de geocoder https://github.com/alexreisner/geocoder
Na verdade, no seu modelo Shop.rb, você precisará adicionar o seguinte para garantir que os campos de longitude e latitude sejam atualizados em sua mesa de loja toda vez que um usuário atualiza o endereço em sua visualização.
GemFile
gem 'geocoder', '~> 1.4'
Você deve adicionar dois campos à mesa da loja, longitude e latitude, certifique -se de que ambos sejam flutuadores, faça a migração se você ainda não fez isso.
Assumindo que address
é um campo e existe na sua mesa de loja, e assumindo que location.html.erb
é uma visão em sua loja e nessa visão você tem algo assim
<%= f.text_field :address, placeholder: "Your Shop's Address", class: "form-control", required: true, id: "shopaddress" %>
Eu também assumi que, quando você criou seu modelo de loja, adicionou a propriedade active:boolean
e user:references
Saber é que uma loja é ativa ou não, e saiba a qual usuário a loja pertence. Então, um usuário tem muitas lojas.
O Shopddress de ID, estou incluindo aqui, caso você queira usar a API GeoComplete Gem com o Google Maps com a Biblioteca de Places. Mas você não precisa disso lá.
Dentro shop.rb
geocoded_by :address
# Will Update if changed
after_validation :geocode, if: :address_changed?
Obviamente, no seu controlador, você deseja garantir que quem esteja atualizando o endereço seja autorizado primeiro e depois execute os métodos. Então, em vez de ter que se repetir. Você provavelmente vai querer criar algo assim no seu controlador de loja.
Dentro shops_controller.rb
class ShopsController < ApplicationController
# If your shop owners are creating many shops you will want to add
#your methods here as well with index. Eg. :create, :new
# In case you have a view shop page to show all people
before_action :set_shop, except: [:index]
before_action :authenticate_user!, except: [:show]
# I am assuming that you also want to update other fields in your
#shop and the address isn't the only one.
before_action :is_user_authorised, only: [:name_x, :name_y, :name_z, :location, :update]
def index
@shops = current_user.shops
end
def show
@photos = @shop.photos
@product_reviews = @shop.product_reviews
end
def name_x
end
def name_y
end
def name_z
end
def location
end
def update
new_params = shop_params
# To ensure the shop is actually published
new_params = shop_params.merge(active: true) if is_shop_ready
if @shop.update(new_params)
flash[:notice] = "Saved..."
else
flash[:alert] = "Oh oh hmm! something went wrong..."
end
redirect_back(fallback_location: request.referer)
end
private
def set_shop
@shop = Shop.find(params[:id])
end
def is_user_authorised
redirect_to root_path, alert: "You don't have permission" unless
current_user.id == @shop.user_id
end
# You can play with this here, what defines a ready shop?
def is_shop_ready
!@shop.active && !@shop.name_x.blank? &&
!@shop.name_y.blank? && !@shop.name_z.blank? &&
!@shop.address.blank?
end
# Here you are allowing the authorized user to require her shop and it's properties, so that she can update them with update method above.
# eg_summary, eg_shop_type, eg_shop_name are just additional #example properties that could have been added when you iniitially created your Shop model
def shop_params
params.require(:shop).permit(:address, :active, :eg_shop_name, :eg_shop_summary, :eg_shop_type)
end
end