A continuación vamos a añadir un segundo modelo a la aplicación. Este nuevo modelo se encargará de gestionar los comentarios de los artículos.
6.1. Generando el modelo
Para generar el nuevo modelo usaremos el mismo generador que se explicó
anteriormente para el modelo Article
. Esta vez se creará un modelo llamado
Comment
que gestionará los comentarios de los artículos. Para crearlo,
ejecuta el siguiente comando:
$ bin/rails generate model Comment commenter:string body:text article:references
Como resultado de este comando se generarán cuatro archivos:
File | Purpose |
---|---|
db/migrate/20140120201010_create_comments.rb |
Archivo de migración para crear la tabla comments en la base de datos (en tu caso el nombre del archivo será ligeramente diferente) |
app/models/comment.rb |
El modelo Comment |
test/models/comment_test.rb |
Los tests del modelo |
test/fixtures/comments.yml |
Comentarios de prueba para los tests |
Primero echa un vistazo al archivo app/models/comment.rb
:
class Comment < ActiveRecord::Base
belongs_to :article
end
Su contenido es muy similar al del modelo Article
generado anteriormente. La
única diferencia es la línea belongs_to :article
, que configura una relación
para Active Record. En la próxima sencción se explican estas relaciones entre
modelos.
Además del modelo, Rails genera un archivo de migración para crear la tabla correspondiente en la base de datos:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :commenter
t.text :body
# this line adds an integer column called `article_id`.
t.references :article, index: true
t.timestamps
end
end
end
La línea t.references
crea una columna de tipo clave foránea para
establecer la relación entre los dos modelos. Además se crea un índice para
esta columna. Como ya tenemos todo preparado, ejecuta el siguiente comando:
$ bin/rake db:migrate
Rails es lo bastante inteligente como para ejecutar solamente las migraciones que todavía no se han ejecutado en la base de datos que se está utilizando. Así que el resultado de ejecutar el comando será:
== CreateComments: migrating ================================================= -- create_table(:comments) -> 0.0115s == CreateComments: migrated (0.0119s) ========================================
6.2. Asociando modelos
Las asociaciones de Active Record permiten declarar las relaciones que existen entre dos modelos. En el caso de los comentarios y los artículos, las relaciones se podrían escribir de esta manera:
- Cada comentario pertenece (en inglés, "belongs to") a un artículo.
- Un artículo puede tener muchos (en inglés, "have many") comentarios.
Si te fijas un poco, la sintaxis que utiliza Rails es prácticamente la misma
que como se describen las relaciones en inglés. Recuerda la línea del modelo
Comment
(archivo app/models/comment.rb
) que establece la relación con
Article
:
class Comment < ActiveRecord::Base
belongs_to :article
end
Ahora edita el archivo app/models/article.rb
para definir el otro extremo de
la relación:
class Article < ActiveRecord::Base
has_many :comments
validates :title, presence: true,
length: { minimum: 5 }
end
Gracias a estas dos declaraciones (belongs_to
y has_many
), Rails puede
hacer casi todo el trabajo automáticamente. Si por ejemplo tienes una variable
de instancia llamada @article
que contiene un artículo, puedes obtener todos
sus comentarios mediante la isntrucción @article.comments
.
Nota Consulta el artículo Active Record Associations para obtener más información sobre las asociaciones.
6.3. Añadiendo una ruta para los comentarios
En primer lugar debemos añadir una nueva ruta para que Rails sepa dónde
queremos navegar para ver los comentarios. Para ello, abre el archivo
config/routes.rb
y haz que tenga el siguiente contenido:
resources :articles do
resources :comments
end
Esta configuración crea la ruta comments
dentro de la ruta articles
que
definimos anteriormente. Esta es otra forma de establecer la relación entre
los dos modelos.
Nota Para obtener más información sobre el enrutamiento, consulta la guía Routing Guide.
6.4. Generando un controlador
El modelo ya está creado así que ahora podemos dedicarnos a su controlador asociado. De nuevo utilizaremos el comando que genera controladores:
$ bin/rails generate controller Comments
Como resultado de este comando se generan seis archivos y un directorio vacío:
Archivo/Directorio | Propósito |
---|---|
app/controllers/comments_controller.rb |
El controlador Comments |
app/views/comments/ |
Direcotrio donde guardar las vistas del controlador |
test/controllers/comments_controller_test.rb |
El test funcional del controlador |
app/helpers/comments_helper.rb |
El helper para las vistas relacionadas con los comentarios |
test/helpers/comments_helper_test.rb |
Test unitario para el helper |
app/assets/javascripts/comment.js.coffee |
Archivo CoffeeScript para las vistas del controlador |
app/assets/stylesheets/comment.css.scss |
Hoja de estilos CSS para las vistas del controlador |
Como sucede en cualquier blog de Internet, los usuarios podrán añadir
comentarios en los artículos y después de hacerlo, se les redirigirá a la
página de ese mismo artículo para que puedan ver su comentario publicado. Por
eso el controlador CommentsController
deberá incluir un método para crear
comentarios y para borrar todos los comentarios de spam que lleguen.
Así que en primer lugar vamos a modificar la plantilla que muestra los
artículos (archivo app/views/articles/show.html.erb
) para que deje crear
nuevos comentarios:
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<h2>Add a comment:</h2>
<%= form_for([@article, @article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
| <%= link_to 'Edit', edit_article_path(@article) %>
Este código añade un formulario en la página show
de los artículos para
poder crear nuevos comentarios llamando a la acción create
del controlador
CommentsController
. En este caso el método form_for
utiliza un array que
crea rutas anidadas de tipo /articles/1/comments
.
El siguiente paso consiste en crear la acción create
en el archivo
app/controllers/comments_controller.rb
:
class CommentsController < ApplicationController
def create
@article = Article.find(params[:article_id])
@comment = @article.comments.create(comment_params)
redirect_to article_path(@article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
Este controlador es un poco más complejo del que creamos para los artículos.
Esta es una de las consecuencias de anidar relaciones. Cada petición
relacionada con un comentario debe contener una referencia al artículo con el
que está relacionado. Por eso tenemos que buscar primero el modelo Article
que está relacionado con el comentario.
Además, la acción utiliza algunos de los métodos disponibles para las
relaciones. Así por ejemplo se utiliza el método create
sobre
@article.comments
para crear y guardar un comentario. Esto hace que el
comentario esté automáticamente relacionado con este artículo específico.
Después de crear el nuevo comentario, se redirige al usuario de nuevo a la
página que meustra el artículo original mediante el helper
article_path(@article)
. Como acabamos de ver, este helper llama a la
acción show
del controlador ArticlesController
, que a su vez renderiza la
plantilla show.html.erb
. Como esta es la plantilla en la que se debe mostrar
el nuevo comentario, añade lo siguiente en el archivo
app/views/articles/show.html.erb
:
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<h2>Comments</h2>
<% @article.comments.each do |comment| %>
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<% end %>
<h2>Add a comment:</h2>
<%= form_for([@article, @article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Edit Article', edit_article_path(@article) %> |
<%= link_to 'Back to Articles', articles_path %>
Ahora ya puedes añadir artículos y comentarios en el blog y cada contenido se muestra en el lugar adecuado.