Adding Subscribers to Products
In order to notify users that a product is back in stock, we need to keep track of these subscribers.
Let’s generate a model called Subscriber to store these email addresses and associate them with the respective product.
$ bin/rails generate model Subscriber product:belongs_to email
Then run the new migration:
$ bin/rails db:migrate
By including product:belongs_to
above, we told Rails that subscribers and
products have a one-to-many relationship, meaning a Subscriber “belongs to” a
single Product instance.
A Product, however, can have many subscribers, so we then add
has_many :subscribers, dependent: :destroy
to our Product model to add the
second part of this association between the two models. This tells Rails how to
join queries between the two database tables.
class Product < ApplicationRecord has_many :subscribers, dependent: :destroy has_one_attached :featured_image has_rich_text :description
validates :name, presence: true validates :inventory_count, numericality: { greater_than_or_equal_to: 0 }end
Now we need a controller to create these subscribers. Let’s create that in with the following code:
class SubscribersController < ApplicationController allow_unauthenticated_access before_action :set_product
def create @product.subscribers.where(subscriber_params).first_or_create redirect_to @product, notice: "You are now subscribed." end
private
def set_product @product = Product.find(params[:product_id]) end
def subscriber_params params.expect(subscriber: [ :email ]) endend
Our redirect sets a notice in the Rails flash. The flash is used for storing messages to display on the next page.
To display the flash message, let’s add the notice to inside the body:
<html> <!-- ... --> <body> <div class="notice"><%= notice %></div> <!-- ... --> </body></html>
To subscribe users to a specific product, we’ll use a nested route so we know
which product the subscriber belongs to. In change
resources :products
to the following:
resources :products do resources :subscribers, only: [ :create ] end
On the product show page, we can check if there is inventory and display the amount in stock. Otherwise, we can display an out of stock message with the subscribe form to get notified when it is back in stock.
Create a new partial at and add the following:
<% if product.inventory_count? %> <p><%= product.inventory_count %> in stock</p><% else %> <p>Out of stock</p> <p>Email me when available.</p>
<%= form_with model: [product, Subscriber.new] do |form| %> <%= form.email_field :email, placeholder: "you@example.com", required: true %> <%= form.submit "Submit" %> <% end %><% end %>
Then update to render this partial after the
cache
block.
<%= render "inventory", product: @product %>
- Preparing Ruby runtime
- Prepare development database