Here's the "cheatsheet" for all of the available actions for a resource, from the last lesson:

Action Use Case HTTP Method Path
index Read (display) a list of all tacos GET /tacos
show Read (display) information on a single taco GET /tacos/123
new A form to create a new taco GET /tacos/new
create Receives the information from the "new" form and creates the new taco POST /tacos
edit A form to update an existing taco GET /tacos/123/edit
update Received the information from the "edit" form and updates the existing taco PATCH /tacos/123
destroy Processes a request to destroy an existing taco DELETE /tacos/123

In this lesson, we're going to concentrate on creating new records. As briefly explained in the last lesson, creating new instances of a resource involves two actions – the new and create actions. This is simply due to the nature of HTML – we need to display a form to fill out – the new action – and also need to have an action to accept the form data as parameters, do the work of creating the new record in the database, and send the user on their way – the create action.

The new Action and Rails' Form Helpers

Let's continue building out our CRM application and give the user the ability to create a new company. Here's the code for both the controller and view:

def new
  render :template => "companies/new"
end
In app/controllers/companies_controller.rb

To break it down, we have a new action defined in our controller, and the only thing happening is that we're rendering the view template file which will have the form. Note that the new doesn't save anything to the database; it's just the request to display the form to the user.

Moving over to the view, we see a fairly vanilla HTML form with the fields we want to save for a company.

<form action="/companies" method="post">
  <p>
    <label for="name">Name</label>
    <input type="text" name="name">
  </p>
  
  <p>
    <label for="city">City</label>
    <input type="text" name="city">

    <label for="state">State</label>
    <input type="text" name="state">
  </p>

  <button>Submit</button>
</form>
In app/views/companies/new.html.erb

We can also see that this form is designed to submit to the create action. How do we know it's the create action? Recall that the create action is the combination of the pluralized resource name as the URL (i.e. /companies) and the HTTP POST method.

The create Action

Try filling out and submitting the form!

That's ok – we don't fear errors! Of course, Rails is letting us know that we don't have a create action in our controller, so let's create one:

def create
end

And let's go back, fill out, and submit the form again... and this time, nothing happens.

This is because we've done something a little different this time than we've done before. Remember that, in the past, when we've gotten this error, we solved it by creating a view file – that is, if we were to add a file called create.html.erb in our app/views/companies/ folder, this error would go away. But that's not what we want in this case. Not all actions have views! In this case, we want our action to be a method in our controller that only does three things:

  • Grab the information the user filled out in the form
  • Use the information to create a new Company and save it to the database
  • Send the user back to the full list of companies

Let's work backwards and add a line of code to our action that will do the last thing – send the user back to the list of companies.

def create
  redirect_to "/companies"
end

Submit the form again, and we'll notice that we're back at the list of companies. But the new company we tried to create isn't there – of course, this is because we didn't do anything with the data submitted.

Remember how we've previously talked about the transfer of state between pages/actions on the web? And how each request-response cycle doesn't really know about the previous ones, or really anything about the order things have happened in the user's journey? The transition from new to create that we're doing here is really no different. We have to get the data that the new action has sent to our create action, and that's done in the same way we've done before – through the params hash.

Let's comb through our server log (that's the output from the rails server command) and look for the POSTing of the form. We'll find this:

Started POST "/companies" for ...
Processing by CompaniesController#create as HTML
  Parameters: {"name"=>"Netflix", "city"=>"Los Gatos", "state"=>"CA"}

Ah! The data the user has typed into the form is, indeed, in the params hash. So let's assign the company values using the params hash in our controller!

def create
  @company = Company.new
  @company["name"] = params["name"]
  @company["city"] = params["city"]
  @company["state"] = params["state"]
  @company.save
  redirect_to "/companies"
end

If we go back, fill out the form, and submit... success!

Try it!

What better way to get some practice with creating records than by creating our own social network!

  • You'll find that there's a PostsController and an index action/view set up already in the project.
  • Add a new.html.erb view and corresponding new action in the controller; create a form in new.html.erb that will allow the end-user to input a post's author, body, and image (see db/schema.rb). author is the author's name (e.g. Brian), body is the text body of the post (e.g. Don't these tacos look awesome?) and image is simply the address of an image on the Internet.
  • For example, we can head over to https://unsplash.com/ and search for square images of tacos – right-click and grab the URL of the image.
  • Add a create action to accept values entered into the form and redirect back to the page with all posts.

Handling Relationships

Creating a company is a good example of a single-model form. But what about contacts? Contacts belong to a company, and the contacts table has a contact_id to fill in – how do we handle a situation like this?

There are a couple of good ways to treat a model form that requires a relationship. The first way would be, as we've done already, to use query string parameters to transfer state from one action to the next. For example, we could create a link on, say, the companies/show page that allows our user to "Create a new contact for this company". Let's give that a try.

<p>
  <a href="/contacts/new?company_id=<%= @company.id %>">New contact for <%= @company.name %></a>
</p>
In app/views/companies/show.html.erb

This is fairly straightforward to implement and the user experience is not bad, either. Clicking on the link will take us to the contacts/new action, transferring the company_id of the company for which we want to create a contact. For now, the code in the new action simply renders the view with the form:

def new
  render :template => "contacts/new"
end
In app/controllers/contacts_controller.rb

And then we can add a form in the view with inputs for the contact data like this:

<h1>New Contact</h1>

<form action="/contacts" method="post">
  <p>
    <label for="first_name">First Name</label>
    <input type="text" name="first_name" id="first_name">
    <label for="last_name">Last Name</label>
    <input type="text" name="last_name" id="last_name">
  </p>
  <p>
    <label for="email">Email</label>
    <input type="text" name="email" id="email">
    <label for="phone_number">Phone</label>
    <input type="text" name="phone_number" id="phone_number">
  </p>
  <p>
    <label for="company_id">Company ID</label>
    <input type="text" name="company_id" id="company_id">
  </p>
  <button>Submit</button>
</form>
app/views/contacts/new.html.erb

And the create action in the controller with code assigning each value and redirecting back to the company.

def create
  @contact = Contact.new
  @contact["first_name"] = params["first_name"]
  @contact["last_name"] = params["last_name"]
  @contact["email"] = params["email"]
  @contact["phone_number"] = params["phone_number"]
  @contact["company_id"] = params["company_id"]
  @contact.save
  redirect_to "/companies/#{@contact["company_id"]}"
end
In app/controllers/contacts_controller.rb

This will all work, but the user experience is lacking.  The user won't know what the company's id value is; we can't expect them to recognize they just came from /companies/1 and then enter a 1 as the company id.  Instead, we should handle that for them.

Good news, we can!  In the link to this new form, we included the company's id in the query string /contacts/new?company_id=1.  And since it's in the query string, we can access it via the params hash: params["company_id"].  To pre-populate the company_id input, change the html to this:

<p>
  <label for="company_id">Company ID</label>
  <input type="text" name="company_id" id="company_id" value="<%= params["company_id"] %>">
</p>

By adding the value attribute to the input, the form field will already have the company id in it for the user.  However, it probably shouldn't be a user-editable field.  As a text input, the user can see 123 (or whatever the ID of the company is), and has the ability to edit that field.  Instead, there is a different type of HTML form field we can use, one that's designed precisely for this purpose – a hidden field.  Here's what the form HTML looks like with the hidden input:

<h1>New Contact</h1>

<form action="/contacts" method="post">
  <p>
    <label for="first_name">First Name</label>
    <input type="text" name="first_name" id="first_name">
    <label for="last_name">Last Name</label>
    <input type="text" name="last_name" id="last_name">
  </p>
  <p>
    <label for="email">Email</label>
    <input type="text" name="email" id="email">
    <label for="phone_number">Phone</label>
    <input type="text" name="phone_number" id="phone_number">
  </p>

  <!-- transfer state from the previous action to the next -->
  <input type="hidden" name="company_id" value="<%= params["company_id"] %>">

  <button>Submit</button>
</form>
app/views/contacts/new.html.erb

Now, the company_id will be successfully included each step of the way – we've transferred state all the way from the <a> tag to create a new contact for a company, through the form, and finally, to the create action.