CRUD actions for your MVC
A step by step walk-through of setting up the routes in an MVC app, with plant memes
I’ve started my journey into building WebApps with the MVC (Model-View-Controller) architectural pattern. It was a nice, natural step up from our CLI-applications that consisted of models (our Class objects) and very, very simplified views rendered in the command line.
Also upgrading from our CRUD actions are the HTTP verbs for users to interact with what we build. Before getting started with Rails, we used Sinatra to see everything that’s happening under the hood of our applications. So let’s have a look, step by step, of how our newly-introduced Controller will route users around our application for a single model.
As actions are performed in the order they’re defined, its important to write these steps in order they’re written below for a Sinatra application. Becoming familiar with each of these steps will be a huge help for deepening your understanding of how to create your routes in a Rails app.
Step 0: The setup.
Let’s pretend we want an app that keeps track of our houseplants. We’ll want to see our houseplant collection, add new ones, update them (in case their condition changes), and in sadder times, delete them.
Let’s add a few details to our database table:
class CreatePlants < ActiveRecord::Migration
def change
create_table :plants do |t|
t.string :type
t.string :care
t.string :condition
end
end
end
And then our model will inherit from ActiveRecord::Base so we can use the methods that ActiveRecord provides:
class Plant < ActiveRecord::Base
end
Step 1: The index
At each of our steps, we’ll need to keep track of our HTTP method (or verb), and the path we want to send our users to. Let’s start with an Index, where we’ll see all the plants we have in our collection.
On your route, add:
get '/plants' do
@plants = Plant.all
erb :index
end
The above is telling our app that when we go to myamazingwebsite.com/plants
, it will get the file index
.
So what is that instance variable doing? Let’s have a look at the code we need to put on index.erb
so users can see our plant collection when they visit our website:
<%= @plants.each do |plant| %>
<a href="/plants/<%= "#{plant.id}" %><%= plant.type %></a>
<%= plant.condition %>
<% end %>
The instance variable @plants
gives us a shortcut on our index page. We can then use the each
enumerable and display the plant type (with a link to the individual plant’s page via interpolation— we’ll add where that link will go to shortly) and its condition. Declaring the instance variable helps to provide a mental shortcut that we’re looking at many plants. Alternatively, the code on the first line could look like this:
Plant.all.each do |plant|
You can see how much neater and easier to read the first version is. It’s much nicer to enumerate through each
of our plants and not each
of our Plant.all.
Step 2: Let’s get a new plant
A weekend trip to the plant nursery means we picked up another miniature succulent, and we want to add it to our collection. There are two steps we need to add the plant: get a webpage with a form to add its details, and then post its details to our database.
First make the route to get the form:
get '/plants/new' do
erb :new
end
And then make a new.erb
to render our form:
<form action="/plants" method="post">
<label for="plant_type">Plant Type: </label>
<input type="text" name="plant[type]" id="type"> <label for="plant_condition">Plant Condition: </label>
<input type="text" name="plant[condition]" id="condition"> <label for="care">Care steps: </label>
<input type="textarea" name="plant[care]" id="care"> <button type="submit" value="submit" id="submit">Save Plant</button>
</form>
Our form will post to /plants
and take in the parameters we pass to it.
Step 3: Let’s show the plant
Cast your mind back to step 1, when we made an index page and linked each plant name to a page to show the individual plant. However, clicking on the link wouldn’t do anything yet. Why is that? We haven’t yet told our app to get the plant’s show page.
get '/plants/:id' do
@plant = Plant.find(params[:id])
erb :show
end
We now have a way to find the individual plant, by passing in that plant’s id to the Find method. This ID (its primary key) was automatically generated by ActiveRecord when we saved the plant to our database. We then need to render the plant’s details on its show page, so we can see the care steps for that individual plant:
<%= @plant.type %>
<%= @plant.condition %>
<%= @plant.care %>
Again, saving this @plant
to an instance variable allows us to pull up the details of this one instance of plant.
Step 4: Create our new plant in the database
Although we had a form to capture the details of our new plant, we haven’t yet declared a way for that information to be created in the database.
post '/plants' do
plant = Plant.create(params[:plant])
redirect "/plants/#{plant.id}"
end
The above is using the HTTP verb to post the information to our database. For this example, we’re only creating a local variable, plant
, as we only need its scope to be available in this one method. Then, use the ActiveRecord method .create
to make a new instance of Plant and save it to the database. Finally, redirect to the individual plant’s page, by interpolating the plant.id
. This should look familiar from our step 3, when we made our route to get ‘/plants/:id’
.
You may also notice that as the above redirects to our individual plant’s page after it’s created, explains why we had to make our show page before we could make a method to create a plant in the database.
Step 5: Edit our plant’s condition
The calathea has been looking suspiciously droopy, so we need to update its condition. The first step to do so is to get another page with a form. Use the ActiveRecord method find
and store the detail in an instance variable.
get '/plants/:id/edit' do
@plant = Plant.find(params[:id])
erb :edit
end
And then render an edit page:
<form action="/plants/<%= @plant.id %>" method="post">
<input type="hidden" name="_method" value="patch">
<%= @plant.type %>
<label for="condition">Condition: </label>
<input type="text" name="plant[condition]" id="plant[condition]" value="<%= @plant.condition %>" > <label for="care">Care steps: </label>
<input type="textarea" name="plant[care]" id="plant[care]" value="<%= @plant.care %>" ><button type-"submit" value="submit" id="submit">Update plant</button>
</form>
The above form is posting to the specific plant, as you can see in the form action that interpolates that plant’s id
.
Again, for simplicity’s sake, we’ve not introduced some of the functionality you’d probably look to add on your app — for example, the option for conditions as radio buttons rather than a text field.
There is a little bit of trickery we need to allow for having an edit form. To get the capability to apply the functionality to edit (and later, delete), we have to set the input type to “hidden”
and the value, for our action, “patch”
on line 2. Finally, update your config.ru
file to get this functionality:
use Rack::MethodOverride #add this here
run ApplicationController
Step 6: Update the Plant’s Condition
Although our form took in the details of our plant’s new condition, we still need to update our database.
patch '/plants/:id' do
@plant = Plant.find(params[:id])
@plant.update(params[:plant])
redirect "/plants/#{@plant.id}"
end
This time we use the HTTP method patch, to update the row in our database table. Patch
will just update the few specific fields that need to be updated, but alternatively you may also see put, which will update everything.
Since the different pages in our app will need to speak to each other to create a link, we will again need to set an instance variable to the ‘found’ value. Then call the ActiveRecord .update
method on our instance variable, and pass in the params
of the plant. Finally, redirect to that plant’s show page.
Step 7: Delete the plant
Unfortunately even with our best care, one our plants is now sadly deceased. We will have to remove it from the database. First make your route on your controller so it knows what to do when it receives the request to delete the plant:
delete '/plants/:id' do
Plant.destroy(params[:id])
redirect '/plants'
end
Finally, you need a way to delete this individual plant. You can place this code onto either the erb :show
or erb :edit
, so it’s contextually situated with the plant in question.
<form action="/plants/<%= @plant.id %>" method="post">
<input type="hidden" name="_method" value="delete">
<button type="submit" value="submit" name="delete" id="delete">Delete Plant</button>
</form>
As with the edit page, again we have to set the input type to hidden on line 2 so we can expand the button’s functionality to delete.
What do you think?
Let me know your thoughts on my explanation of the different routes for the controller in an MVC app. I’d love to know if you have any feedback or corrections, so please leave a comment below!
Follow philomemedron and planty_hoes for plant memes. Subscribe to my channel for writing on code, design, and lifelong learning.