At this point, we’ve stored records in the database by using the SaveOperation
and Avram::Factory
classes.
To pull the data back out, we will use the Query classes. These query objects are located in your src/queries/
directory.
We can see there are already two files in here; fortune_query.cr
and user_query.cr
.
The most basic query you can run is SELECT * FROM table_name
.
To query all of the users (SELECT * FROM users
) it’s as simple as UserQuery.new
. If you need a specific record,
we can use the find
method and pass in the primary key value for the record we are looking for. (e.g. UserQuery.find(4)
)
The query objects are considered “lazy” which means that just instantiating the class doesn’t execute the query. To
run the query, you’ll either iterate the collection using a method like each
or map
, or you will ask for a single
record like calling find
or first
.
Here are a few simple examples:
# SELECT * FROM users;
UserQuery.new
# SELECT * FROM users WHERE id = 4;
UserQuery.find(4)
When you need to get more granular with your query, you will want to run a WHERE
query. In Lucky each of the columns
on a model have an associated query method that requires the proper type to be passed. This keeps your queries type-safe.
# SELECT * FROM users WHERE email = 'test@test.com';
UserQuery.new.email("test@test.com")
# SELECT * FROM meals WHERE favorite = 't' AND name = 'tacos';
MealQuery.new.favorite(true).name("tacos")
As you can see, the methods are chainable. If the type is Bool
then you must pass a boolean value. Passing in a nil
value
when the type isn’t nilable will cause a compile-time error.
For more information on querying records read the Querying Records guide.
It would be nice to show the last 10 fortunes on our home page. We already have a spot on the page, we just need to add the
code to our Home::Index
action, and then display the results on the Home::IndexPage
page.
Let’s open up our Home::Index
action in src/actions/home/index.cr
. Inside of the else
branch, we will add our query.
# src/actions/home/index.cr
get "/" do
if current_user?
redirect Me::Show
else
# ORDER BY created_at DESC LIMIT 10
latest_fortunes = FortuneQuery.new.created_at.desc_order.limit(10)
# Pass these fortunes to our IndexPage
html IndexPage, fortunes: latest_fortunes
end
end
Since we need to pass data from our action to our page, we must update the Home::IndexPage
so it knows that it needs
this data. Open up the Home::IndexPage
in src/pages/home/index_page.cr
.
At the top of the class we can add our needs
line:
# src/pages/home/index_page.cr
class Home::IndexPage < AuthLayout
needs fortunes : FortuneQuery
def content
# ...our page content
end
end
The key we specified in our action was fortunes
, and the value is an instance of the FortuneQuery
. Keep in mind
that the queries are “lazy”, so at this point, the data still hasn’t been fetched. We must iterate over this data,
or select a single record.
Our needs
on the page gives us a method by the same name fortunes
which we can use to iterate over. Let’s add
some code inside of that “container” div we added:
# src/pages/home/index_page.cr
def content
div class: "px-4 py-5 my-5 text-center" do
# ... previous markup
end
div class: "container" do
# Add this block of code here
div class: "list-group list-group-flush border-bottom scrollarea" do
fortunes.each do |fortune|
link to: Fortunes::Show.with(fortune.id), class: "list-group-item list-group-item-action py-3 lh-tight" do
div class: "d-flex w-100 align-items-center justify-content-between" do
small time_ago_in_words(fortune.created_at), class: "text-muted"
end
div fortune.text, class: "col-10 mb-1 small"
end
end
end
end
end
At this point we should make sure things are still working. Boot your app (lucky dev
) to make sure everything compiles.
Then view the beautiful new list. Everything work? Great!
We’ve started really filling in our app, but we can always do better. Give a little refactor a shot!
Try this…
latest
method in your FortuneQuery
class that fetches the last 10 records created.Home::Index
action to use the new latest
method instead of the limit and ordering.name : String
column to your User
model. (hint: generate a new migration)UserFactory
to generate a random name
.ctrl-k
) for mention of “bulk updating”)