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 emailThen run the new migration:
$ bin/rails db:migrateBy 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 }endNow 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 ]) endendOur 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 ] endOn 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