Hi everyone,
Series navigation
PART 1 Introduction
PART 2 Foundation
PART 3 The Walls
PART 4 The Door
PART 5 Testing
PART 6 Can you fly?
PART 7 The User
Welcome to part 3 of the series but before we jump in let's recap what we did in the second part:
- We defined the requirements to install phoenix
- We verified them one-by-one
- We created a folder to store our project wolf_blog
- We generated our first Phoenix API
Now let's get serious and bring in the tools that make any developer great and also help us manage our project better:
git version control
editor: VSCode, VSCodium(the opensource version of VSCode), Atom, IntelliJ Idea, nano, emacs, vim, others.
Let's dive in the code and follow the instructions from last time and type:
cd ~/Codes/wolf_blog
codium .
The command I typed will import wolf_blog in codium, if you have another editor Dear Reader, please feel free to use that to import our project.
Optional install:
In the editor install elixir extension: Elixir_LS
https://marketplace.visualstudio.com/items?itemName=elixir-lsp.elixir-ls
If you can't find your editor above, search for Elixir-lsp in your editor Plugin Management Installer.
Section 1
Let's Git it
In the terminal navigate to the project
cd ~/Codes/wolf_blog
Word of warning you need to have git initialized on your system before you type:
git init
If you don't know how I recommend this resource
https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup
Also verify that you created the ssh key needed to be able to use git from the terminal
Resource here https://help.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh
With that out of the way let's initialize git with:
git init
Now git has been added to our project, but we still need to create our git repo.
For this, I will use hub
, https://hub.github.com/ and create the repo directly from the terminal, without visiting my GitHub page, pretty useful right?
So type
hub create
After this command Dear Reader you should get an output like this:
hub create
github.com username: wolfiton
github.com password for wolfiton (never stored):
Updating origin
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'github.com,ip' (RSA) to the list of known hosts.
Enter passphrase for key '/home/dan/.ssh/id_rsa':
https://github.com/wolfiton/wolf_blog
That will confirm that our project is on Github now.
Optional section for custom configurations for database access
Dear reader, if you are using another username then Postgres to work with the PostgreSQL database now it is a good time to make modifications in the following files:
/config/dev.exs
/config/test.exs
In these files, you will find something similar to this
Content in dev.exs
# Configure your database
config :wolf_blog, WolfBlog.Repo,
username: "postgres", # Change the username to your postgres username
password: "postgres",
database: "wolf_blog_dev",
hostname: "localhost",
show_sensitive_data_on_connection_error: true,
pool_size: 10
Content in test.exs
# Configure your database
config :wolf_blog, WolfBlog.Repo,
username: "postgres", # Change the username to your postgres username
password: "postgres",
database: "wolf_blog_test",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox
Also, the following are comments in elixir code:
# Single line comment
"""
Multiline comment
"""
It is time to add our dependencies(packages) to our project, in phoenix this is done by hand.
So open up mix.exs from the root of the project(top-level) and add:
deps do
[
{:phoenix, "~> 1.4.11"},
{:phoenix_pubsub, "~> 1.1"},
{:phoenix_ecto, "~> 4.0"},
{:ecto_sql, "~> 3.1"},
{:postgrex, ">= 0.0.0"},
{:gettext, "~> 0.11"},
{:jason, "~> 1.0"},
{:plug_cowboy, "~> 2.0"},
# All the above packages come from Phoenix inital config when we
# generated our project.
# Add the new packages below
{:absinthe, "~> 1.4"},
{:absinthe_plug, "~> 1.4"}, #made a modification here
{:absinthe_phoenix, "~> 1.4"}
]
end
Note: The comments have been added for clarity and a helper guide to Know where to add packages. They are not recommended to be added to your own project.
Now if you have installed the Elixir_LS extension you should see that 3 packages are fetched or installed.
If not please type the following, in the terminal by navigating to cd ~/Codes/wolf_blog
:
Then type
mix deps.get
Let's also generate our database with this:
mix ecto.create
Section 2
Organization is key
We are all set to make our branch and branch features now.
Create the develop branch using this command:
git checkout -b develop
git pull
The next step is to open .gitignore from the root of the project and add this line:
Below the last line
.elixir_ls
and save the file.
The
.elixir_ls
line in .gitignore, is only required if you installed Elixir_LS plugin for your IDE(editor). If not, you can safely ignore it and move on to the next steps below.
Then use
git add .
git commit -m "First commit to develop"
git push --set-upstream origin develop
Good, create a new branch
git checkout -b 01-The_Wall
Section 3
Bring the code out
Generate a new context schema and migration using this
mix phx.gen.context Blog Post posts title:unique body
mix ecto.migrate
What was that, Wolfiton?!
Phoenix uses a migration that you can see here
~/Codes/wolf_blog/priv/repo/migrations/_create_posts.exs
too tell the database the structure of the posts table.
Update
Thanks @TheEndIsNear, for suggesting on slack the following
I'm reading part 3 now. Found a small typo "The cotext content is here: lib/wolf_blog/blog.ex " You want "The context content is here: lib/wolf_blog/blog.ex" (edited)
You might also want to mention that the migration file includes the date that the migration was created
Also, migrations include the date when the migration was created, so it is very easy to sort understand in which order they will run.
Migration _create_posts.exs content looks like this
defmodule WolfBlog.Repo.Migrations.CreatePosts do
use Ecto.Migration
def change do
create table(:posts) do
add :title, :string #This will create a title column in the
# posts table
add :body, :string # Create the body column in the posts table
timestamps() # create updated_at, created_at columns
end
create unique_index(:posts, [:title]) # create unique index for title
end
end
Moving forward we have in lib/wolf_blog/blog/post.ex
defmodule WolfBlog.Blog.Post do
use Ecto.Schema
import Ecto.Changeset
schema "posts" do
field :body, :string
field :title, :string
timestamps()
end
@doc false
def changeset(post, attrs) do
post
|> cast(attrs, [:title, :body])
|> validate_required([:title, :body])
|> unique_constraint(:title)
end
end
Here we have the schema, this is used to set the rules and validations for the database.
Using Ecto changeset the schema will allow or disallow to certain values to pass or get rejected.
You can think about it as a Ticket inspector that is very flexible.
Explanations
For our schema, we have the following rules in place:
- the title column needs to be unique and a string "string"
unique_constraint(:title)
field :title, :string
- the body needs to be a string
field :body, :string
- They are also required so if we don't add one of them we will get an error.
validate_required([:title, :body])
I hope that these explanations help in understanding the Phoenix Schema.
The context is used to group similar schemas and make some operations on the database.
The content for our content is here: lib/wolf_blog/blog.ex
When we used the generator, we got the following operations on our posts table:
get a post by id
def get_post!(id)
get all the posts
def list_posts
create a post
def create_post
update a post
def update_post
delete a post
def delete_post
They all use the Ecto library to interact with the Postgres database.
Let's add our changes to the 01-The_Wall branch and end part 3 here.
git add .
git commit -m "Generated the Blog Context with the Post Schema"
git push --set-upstream origin 01-The_Wall
See you next time Dear reader, when we are going to build our first query and test.
Section 4
Update:(Recommended)
As suggested in the comments by @kurisusan
, we will modify our create_posts.ex
migration file so that we can have more than 255 characters in our body field.
The field :body :string
will be converted to :body :text
.
You maybe are wondering now Dear Reader, who can we do that?
Simple Wolfiton!, we are just going to edit the original file.
That could be a solution but if we want our migrations to be regenerated later, we will have to make the modification each time.
Not very pleasant is it?
So let's explore our other options, another option is to create an alter migration that will make the modification for us. So the edit on the body field will be taken care of by ECTO.
This sound like a plan, let's implement it:
Navigate to the root of our project cd ~/Codes/wolf_blog
List all branches
git branch -a #this command will show us all our current branches
Because we are adding a different purpose to our field considered an improvement we are creating a new branch for this feature.
git checkout -b 02-The_Wall
Than type:
mix ecto.gen.migration body_string_to_body_text
Now let's open our migration in ~/Codes/wolf_blog/priv/repo/migrations/
numbers_body_string_to_body_text.exs
The content should look like this:
defmodule WolfBlog.Repo.Migrations.BodyStringToBodyText do
use Ecto.Migration
def change do
end
end
Let's change it to this
Warning! The comments below are added for clarity they are not recommended
defmodule WolfBlog.Repo.Migrations.BodyStringToBodyText do
use Ecto.Migration
def change do
alter table(:posts) do # alter will modify the posts table
modify :body, :text # modifies the body column from string to text
end
end
end
Than run
mix ecto.migrate
Good now, all we have to do
git add .
git commit -m "Improvement body string changed to the body text"
git push --set-upstream origin 02-The_Wall
We are done Dear reader, I hope you enjoyed this update.
Thank you again @kurisusan for your suggestion in the comments:
Nice post to read. Really clear and easy to understand.
About the posts migration file I have a suggestion.
If the “body” column will store string of more than 255 characters length, before running the migration I would suggest to change its type to ":text".
This will allow to store post with unlimited length body in the database. > The Post schema however is ok with the type ":string" for the body field.
Also any questions, comments or suggestions are welcomed so please feel free Dear Reader to write. I will always read them.
Credits:
https://marketplace.visualstudio.com/items?itemName=elixir-lsp.elixir-ls
https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup
https://help.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh
https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Schema.html
@kurisusan
for improvements :body :string
to :body :text
, Now the body field can use more 255 characters.
Thanks @TheEndIsNear for improvements on migration explanations regarding the fact that migrations include the date and grammar correction.
Top comments (2)
Nice post to read. Really clear and easy to understand.
About the posts migration file I have a suggestion.
If the “body” column will store string of more than 255 characters length, before running the migration I would suggest to change its type to ":text".
This will allow to store post with unlimited length body in the database. The Post schema however is ok with the type ":string" for the body field.
I will add an alter table to the migration to change this using a generated migration, tomorrow.
Thanks @kurisusan for the suggestion.