Created
August 31, 2010 19:35
-
-
Save andresgutgon/559597 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# PROBLEMA: | |
# ¿Cómo hacer que N tipos diferentes de objetos compartan atributos/campos comunes? | |
# Para resolver el problema se me ocurren 3 opciones | |
# A) CON RELACIONES POLIMORFICAS | |
# B) STI | |
# C) TODO EN UNA UNICA TABLA | |
# | |
# ----------------------------------------------------------------------------------------------------------- | |
# A) CON RELACIONES POLIMORFICAS: | |
# NOTA: No estoy seguro, pero creo que esto mismo funcionaria si fuera has_one en lugar de has_many. | |
# Que en nuestro caso tiene mas sentido. | |
# class Journal | |
# has_many :publications, :as => :publicable | |
# end | |
# | |
# class Book | |
# has_many :publications, :as => :publicable | |
# end | |
# | |
# class Publication | |
# belongs_to :publicable, :polymorphic => true | |
# end | |
# De esta manera, cuando creemos un Book o un Journal tendremos asociada la tabla publicaciones. | |
# Ventaja: | |
# 1. Escala: Todos los tipo de publicacion que quieras | |
# 2. Clara separacion de rutas (site.com/books y site.com/journals,...) | |
# Desventajas: | |
# 1. Estas haciendo relaciones mas complejas para gestionar las publicaciones de la pagina. | |
# Muchas tablas, modelos, controladores y vistas | |
# ----------------------------------------------------------------------------------------------------------- | |
# B) STI | |
# Este articulo explica bien el concepto de STI | |
# http://jumpstartlab.com/resources/rails-jumpstart/jscontact/i3-remodeling-people-into-contacts-with-sti/ | |
# Leetelo y entiendelo entero, y habras aprendido STI en Rails. A mi me sirvio, lo que pasa es que me | |
# parecio mas trabajo que la opción C | |
# ----------------------------------------------------------------------------------------------------------- | |
# C) TODO EN UNA UNICA TABLA | |
# Todo en una sola tabla y en un solo modelo (Publication) | |
# VENTAJAS: | |
# 1. Solo tienes que gestionar un modelo | |
# DESVENTAJAS: | |
# 1. Es un poco lio para entender | |
# 2. Mas de cuatro tipos supone bastante lio en las validaciones y en los formularios. Si solo vas a tener tres o cuatro puedes probarlo. | |
# OBJETIVO: Quiero poder crear y editar publicaciones, pero de tres tipos distintos, y cada tipo tendra | |
# campos diferentes | |
# Al hacerlo asi te encontraras con que los registros de un tipo dejan campos vacios Porque a la hora de | |
# crearlos no los validaste como necesarios. En fin, basurilla en la BBDD, pero se puede vivir con ello. | |
# Alla voy. | |
# 1. Haz un selector de tipo de publicación | |
# Cuando quieras crear una publicacion debes apuntar a esta URL | |
< %= link_to "Crea publicacion", select_pub_type_path % > | |
# routes.rb | |
map.resources :publications, :member => { :select_pub_type => :get} | |
# publications_controller.rb | |
def select_pub_type | |
@pre_publication = PrePublication.new | |
end | |
# Lo que hay que contar aqui es que me he creado un modelo que no apunta a una tabla de la BBDD, solo me | |
# sirve como paso intermedio para almacenar el tipo de publicacion antes de que cree la verdadera publicacion | |
# app/models/pre_publication.rb | |
# Nota que heredo de Tableless porque estoy en Rails 2.3.8 y las validaciones no estan accesibles si no heredo de ActiveRecord | |
# Si trabajas con Rails3 lo tendras mas facil :) | |
# Tableles es un plugin que trae las validaciones de rails a una clase que no hereda de ActiveRecord | |
class PrePublication < Tableless | |
column :pub_type, :string | |
column :published, :boolean | |
PUB_TYPES = { :books => "Libros", :journals => "Revistas",....} | |
validates_presence_of :pub_type | |
end | |
# app/views/publications/select_pub_type.html.erb | |
# Con el objeto @pre_publication creado, mostramos los tipos de publicaciones que ofrecemos al usuario | |
# NOTA: LA sintaxis del form no es la de Rails porque yo uso Formtastic, pero funciona igual. | |
<% semantic_form_for @pre_publication, :url => redirect_to_new_publication_user_path do |form| %> | |
<% form.inputs do %> | |
<%= form.input :pub_type, :as => :radio, :value_as_class => true, :collection => PUB_TYPES %> | |
<%= form.input :published, :checked => true %> | |
<% end %> | |
<% form.buttons do %> | |
<%= form.button :next %> | |
<% end %> | |
<% end %> | |
# De este archivo hay que destacar dos cosas | |
# 1. La URL donde apunta el form => redirect_to_new_publication_path | |
# 2. De donde saco el tipo de publicaciones que permito. Esto sale de la variable que he creado en app/models/pre_publication.rb | |
# 1. Lo primero es un metodo que me redirige al metodo new de la publicacion, pero con el tipo | |
# de publicacion seteado | |
# routes.rb | |
map.resources :publications, :member => { :redirect_to_new_publication => :post} | |
# publications_controller.rb | |
# NOTA: Si el objeto @pre_publication es valido deacuerdo con las validaciones hechas en app/models/pre_publication.rb | |
# lo redirijo a new, pero con el tipo de publicacion como param de la URL ej.: site.com?type=book | |
# Si no lo mando de vuelta a select_pub_type | |
def redirect_to_new_publication | |
@pre_publication = PrePublication.new(params[:pre_publication]) | |
if @pre_publication.valid? | |
redirect_to new_publication_path(@user,:pub_type => @pre_publication.pub_type) | |
else | |
# request.post? | |
render :template => "publications/select_pub_type" | |
end | |
end | |
# publications_controller.rb | |
def new | |
if params[:pub_type] | |
pub_type = params[:pub_type] | |
published = params[:published] ? true : false | |
if PrePublication::PUB_TYPES.include?(pub_type) | |
@publication = @publication.new(:pub_type => pub_type, :published => published) | |
else | |
flash[:error] = "Elige un tipo de publicacion permitido" | |
redirect_to select_pub_type_path | |
end | |
else | |
flash[:error] = "Primero elige el tipo de publicacion" | |
redirect_to select_pub_type_path | |
end | |
end | |
# app/views/publications/new.html.erb | |
# Este archivo es muy complicado, por eso esta idea solo es buena si solo tienes tres o cuatro tipos diferentes de publicaciones | |
# Para que te hagas una idea, a la hora de mostrar los campos lo que hago es mostrar lo comunes (nombre, autor,...) como en cualquier | |
# otro formulario. | |
# Pero para los campos que son propios solo de una o dos tipos de publicaciones hago esto: | |
# En new instamcio un objeto del tipo Publication, por lo que todos los metodos publicos de la clase publication.rb los | |
# tengo accesibles en la vista. Asi es como miro si para ese tipo de publicacion le tengo que enseñar al usuario ese campo. | |
<% if @publication.have_editors? % > | |
<%= form.input :editors % > | |
<% end % > | |
# Nota el metodo .have_editors? este tipo de metodos los coloco en el modelo publication.rb | |
# En este archivo esta jugo por eso te lo pongo casi entero y miras como valido o no los campos en funcion del tipo de publicación. | |
# publication.rb | |
class Publication < ActiveRecord::Base | |
# validations | |
validates_presence_of :title, :abstract | |
validates_presence_of :pub_pages, :if => Proc.new{|model| model.have_pages?} | |
validates_presence_of :media_title, :if => Proc.new{|model| model.have_media_title?} | |
validates_presence_of :publisher, :if => Proc.new{|model| and model.have_publisher?} | |
validates_format_of :pub_pages, :with => /\A\d{1,5}(-\d{1,5})?\z/, | |
:if => Proc.new{|model| model.have_pages?} | |
# Metodos para validar y mostrar en funcion del tipo de publicacion | |
def have_pages? | |
["journal", "article_or_chapter_book"].include?(self.pub_type) | |
end | |
def have_media_title? | |
["journal", "article_or_chapter_book"].include?(self.pub_type) | |
end | |
def have_publisher? | |
["book", "article_or_chapter_book"].include?(self.pub_type) | |
end | |
def have_editors? | |
["book", "article_or_chapter_book"].include?(self.pub_type) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment