Handling Valid Input
When the user submits valid input through the Contact Us form, we need to perform a few actions:
- Store the contact data in the database.
- Send a confirmation email to the user (a simple static confirmation that their request was received.)
- Redirect the user to /contacts/thanks and display a simple “Thank you” message.
Storing the contact details in the database
To store contact requests in the database, we need to create a new table. First, we will define the schema for this table.
Create a table
Navigate to your project root and create a new directory named schema
.
mkdir app/schema
Inside the schema
directory, create a contact.yml
file:
app/schema/contact.yml
properties:
- name: email
- name: body
By default, properties in this YAML file are treated as strings, so you don't need to specify the data types explicitly. This schema definition is enough to create the table.
Test if the table exists in the database
After defining the schema, you can verify the table creation by accessing the database interface. Open your browser and navigate to http://localhost:3333/database. You should see the new table with email and body columns, both of which are strings:
Create the Execute command
Now that we have our schema in place, let's integrate these actions into our application by updating the necessary files and configurations. Our goal is to validate the contact form and persist the valid data in the database. To achieve this, we will create a Liquid file that executes a GraphQL mutation.
First, create the execute.liquid
file in the following directory:
app/lib/commands/contacts/create/execute.liquid
To populate it, you don’t have to write the execute command from scratch. Instead, let's refer to the core module documentation’s Execute section. Here's the example execute command you can use:
{% liquid
graphql r = 'dummy/create', args: object
assign object = r.record_create
hash_assign object['valid'] = true
return object
%}
Copy the code above from the core module documentation and paste it into your execute.liquid
file. Make the following modifications:
app/lib/commands/contacts/create/execute.liquid
{% liquid
graphql r = 'contacts/create', args: object
assign object = r.record_create
hash_assign object['valid'] = true
return object
%}
This command executes a GraphQL mutation to persist the data and prepares the object for further validation. Let’s break it down:
graphql r = 'contacts/create', args: object
This line uses the graphql tag to execute a GraphQL mutation. The mutation is named contacts/create
, and it takes object
as an argument.
assign object = r.record_create
This line assigns the result of the mutation to the object
variable.
hash_assign object['valid'] = true
This line artificially assigns the valid property of the object
to true
. This is done for consistency, as this property will be used in the check
command.
Invoke the execute command in the create.liquid file
Now that we have the execute command set up, we need to invoke this command within our main create.liquid
file to handle the form submission process. Our goal is to ensure that the execute
command is properly called after the build
and check
commands validate the contact data.
Modify your app/lib/commands/contacts/create.liquid
file:
app/lib/commands/contacts/create.liquid
{% function contact = 'commands/contacts/create/build', object: object %}
{% function contact_after_validation = 'commands/contacts/create/check', object: contact %}
{% if contact_after_validation.valid %}
{% function contact_after_execute = 'commands/contacts/create/execute', object: contact_after_validation %}
{% endif %}
{% return contact_after_execute %}
Let’s see what we do here:
{% function contact = 'commands/contacts/create/build', object: object %}
This line invokes the build
command, passing the object
(the form data). The result is assigned to the contact
variable.
{% function contact_after_validation = 'commands/contacts/create/check', object: contact %}
This line invokes the check
command with the contact
object. The result is assigned to the contact_after_validation
variable.
As you notice, we added a conditional execute command invocation:
{% if contact_after_validation.valid %}
{% function contact_after_execute = 'commands/contacts/create/execute', object: contact_after_validation %}
{% endif %}
If the contact_after_validation
object is valid, the execute command is invoked, passing the validated contact
object. The result is assigned to the contact_after_execute
variable.
{% return contact_after_execute %}
Then the final result after execution is returned.
If you try it, the invalidation won't work correctly. For valid inputs, when the user submits the form, everything will function as expected. However, for invalid inputs, the system will still attempt to return contact_after_execute
even though it hasn't reached that point because contact_after_validation.valid
is false
in this scenario. This means we'll end up returning null
, which results in losing information about validation error messages and the original user input.
A way to fix it would be by returning the correct variable if valid
is false:
{% if contact_after_validation.valid %}
{% function contact_after_execute = 'commands/contacts/create/execute', object: contact_after_validation %}
{% return contact_after_execute %}
{% else %}
{% return contact_after_validation %}
{% endif %}
Instead of these solutions, let’s rename everything to object
:
app/lib/commands/contacts/create.liquid
{% function object = 'commands/contacts/create/build', object: object %}
{% function object = 'commands/contacts/create/check', object: object %}
{% if object.valid %}
{% function object = 'commands/contacts/create/execute', object: object %}
{% endif %}
{% return object %}
Create GraphQL Mutation
The final step is to create a GraphQL mutation to persist the contact data in the database. This involves creating the necessary directories and files to store the GraphQL mutation.
Create the directory structure
Navigate to your project root and create the necessary folders for storing GraphQL files:
mkdir -p app/graphql/contacts
Create the GraphQL mutation file
Inside the app/graphql/contacts
directory, create a file named create.graphql
app/graphql/contacts/create.graphql
Define the mutation
Open app/graphql/contacts/create.graphql
and add the following mutation code:
app/graphql/contacts/create.graphql
mutation contact_create($email: String!, $body: String!) {
record_create(
record: {
table: "contact"
properties: [
{ name: "email", value: $email }
{ name: "body", value: $body }
]
}
) {
id
email: property(name: "email")
body: property(name: "body")
created_at
}
}
Let’s break it down to see what happens:
mutation contact_create($email: String!, $body: String!) {
This line defines the mutation contact_create
and specifies that it requires two parameters: email
and body
, both of which are strings and must be provided (!
indicates non-nullable).
record_create(
record: {
table: "contact"
properties: [
{ name: "email", value: $email }
{ name: "body", value: $body }
]
}
) {
This block specifies the record_create
mutation, which creates a new record in the contact
table. The properties
array defines the fields to be populated with the values of $email
and $body
.
id
email: property(name: "email")
body: property(name: "body")
created_at
These lines define the fields that will be returned after the record is created. This includes the id
, email
, body
, and created_at
timestamp.
Special arguments: args
In the context of platformOS, the args
parameter serves as a special argument that can be either a hash or a string of arguments passed to the GraphQL query.
When passing multiple arguments at once, you can encapsulate them within a JSON string. For example:
{% capture arguments %}
{
"per_page": 20,
"page": 2
}
{% endcapture %}
{% graphql g = "get_models", args: arguments %}
Without using args
, you would need to specify each argument individually like this:
{% graphql g = "get_models", page: 2, per_page: 20 %}
While for a small number of arguments, this may not seem particularly beneficial, it becomes more advantageous when dealing with a larger number of arguments, such as 10 or 20.
To learn more about how args
function, refer to the examples provided in the Liquid Tags chapter of the documentation.
Testing the mutation using GraphiQL
The GraphiQL interface allows you to simulate and verify your GraphQL mutation before fully integrating it into your application. Let’s test the mutation we created!
Navigate to the GraphiQL interface at http://localhost:3333/gui/graphql/. Paste the mutation code you have at app/graphql/contacts/create.graphql
to verify its functionality.
Below the editor, find the section to add query variables. Paste the following JSON code into the variables section for the test:
{
"email": "testemail@email.com",
"body": "Hello! This is my message"
}
Click the execute button and check the response to ensure that the mutation executed successfully. You should see a response containing the ID, email, body, and created_at fields, indicating that the record has been created in the database. The response should look something like this:
{
"data": {
"record_create": {
"id": "9",
"email": "testemail@email.com",
"body": "Hello! This is my message",
"created_at": "2024-06-04T14:04:24.224Z"
}
}
}
To ensure the data has been correctly stored in your platformOS database, navigate to your database interface at http://localhost:3333/database. Search for the newly created contact record to confirm its presence and validate that the email and body fields have been correctly stored.