https://github.com/itdxer/4daysorm
Lua 4Days ORM for sqlite3 and mysql
https://github.com/itdxer/4daysorm
Last synced: about 2 months ago
JSON representation
Lua 4Days ORM for sqlite3 and mysql
- Host: GitHub
- URL: https://github.com/itdxer/4daysorm
- Owner: itdxer
- License: mit
- Created: 2013-08-16T07:57:33.000Z (almost 12 years ago)
- Default Branch: master
- Last Pushed: 2021-03-01T10:46:50.000Z (over 4 years ago)
- Last Synced: 2023-08-03T03:55:12.681Z (almost 2 years ago)
- Language: Lua
- Homepage: http://itdxer.github.io/4DaysORM/
- Size: 69.3 KB
- Stars: 89
- Watchers: 12
- Forks: 31
- Open Issues: 7
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Lua 4Days ORM 10 minutes tutorial #
## Database configuration ##
Before the beginning you should add some simple settings to your database configuration. You must create some global variable `DB`:
```lua
DB = {}
```**Development configurations:**
1. `new` - if this value is `true`, then previous database was removed and new was created (*`true` by default*).
2. `backtrace` - if this value is `true`, than you will be able to see in console all Warnings, Errors and Information messages (*`true` by default*).
3. `DEBUG` - if this value is `true`, you will be able to see in console all SQL queries (*`true` by default*).**Database configurations**
1. `type` - by default `"sqlite3"`. Also it can be:
- `"mysql"` - for MySQL database
- `"postgresql"` - for PostgreSQL database (*implemented soon*)
2. `name` - this is a path to database file for `"sqlite3"`. For other databases this value contains database name. (*by default `"database.db"`*)
3. `username` - database user name (*by default `nil`*)
4. `password` - database password (*by default `nil`*)
5. `host` - database host (*by default `nil`*)
6. `port` - database host port (*by default `nil`*)----------
After setting configurations you can add 2 modules import to your file
```lua
local Table = require("orm.model")
local fields = require("orm.tools.fields")
```## Create table ##
```lua
local User = Table({
__tablename__ = "user",
username = fields.CharField({max_length = 100, unique = true}),
password = fields.CharField({max_length = 50, unique = true}),
age = fields.IntegerField({max_length = 2, null = true}),
job = fields.CharField({max_length = 50, null = true}),
time_create = fields.DateTimeField({null = true})
})
```For every table is created a column `id` with `PRIMARY KEY` field by default.
`__tablename__` is required value which should contain the name of the table.
`__columnCreateOrder__` is optional and can be used to define the order in which the columns will be created in the database table (the value must be a table of column names)Also you can add different settings to your table fields
1. `max_length` - it is a maximum allowable value of symbols that you can use in a string
2. `unique` - if this value is `true ` then all the column's values are unique
3. `null` - can be `true` or `false`. If value is `true` then value in table will be saved as `NULL`.
4. `default` - if you didn't add any value to this field - it is going to be saved as default value.
5. `primary_key` - If you want to add some value as `primary key`, you can set this value as `true`.
6. `escape_value` - If this value is `true` and the column type is a string type special characters will be escaped to prevent sql injection## Types of table fields ##
Supported types of table fields
1. `CharField` - Creates `VARCHAR` field
2. `IntegerField` - Creates `INTEGER` field
3. `TextField` - Creates `TEXT` field
4. `BooleanField` - Creates `BOOLEAN` field
5. `DateTimeField` - Creates `INTEGER` field but brings back `os.date` instance
6. `PrimaryField` - Creates `INTEGER` field with `PRIMARY KEY`
7. `ForeignKey` - Creates relationships between tables.Also you can create your types of table fields. But about it later.
## Create data ##
Try to create a new user:
```lua
local user = User({
username = "Bob Smith",
password = "SuperSecretPassword",
time_create = os.time()
})
```Now you created new user, but it was not added to database. You can add him.
```lua
user:save()
```Now this user with all the information is in database. We can get his `id`
```lua
print("User " .. user.username .. " has id " .. user.id)
-- User Bob Smith has id 1
```## Update data ##
You can change your data:
```lua
user.username = "John Smith"
```This value was changed in model, but it has not been changed in database table.
```lua
user:save()
```Now try to get new username for user:
```lua
print("New user name is " .. user.username) -- New user name is John Smith
```You have updated in database only the column that you changed.
You can also edit columns for the value by another terms:```lua
User.get:where({time_create__null = true})
:update({time_create = os.time()})
```*The conditions will be described in the next chapter*
## Remove data ##
And also you can remove your data from table.
```lua
user:delete()
```You can also delete columns for the value by another terms:
```lua
-- add test user
user = User({username = "SomebodyNew", password = "NotSecret"})
user:save()User.get:where({username = "SomebodyNew"}):delete()
```*The conditions will be described in the next chapter*
## Get data ##
Also we can get data from table. But before this let's create 5 test users.
```lua
user = User({username = "First user", password = "secret1", age = 22})
user:save()user = User({username = "Second user", password = "secret_test", job = "Lua developer"})
user:save()user = User({username = "Another user", password = "old_test", age = 44})
user:save()user = User({username = "New user", password = "some_passwd", age = 23, job = "Manager"})
user:save()user = User({username = "Old user", password = "secret_passwd", age = 44})
user:save()
```And now try get **one of them**:
```lua
local first_user = User.get:first()
print("First user name is: " .. first_user.username)
-- First user name is: First user
```But also we can **get all users** from table:
```lua
local users = User.get:all()
print("We get " .. users:count() .. " users")
-- We get 5 users
```Method `count` returns number of users in the list.
### Limit and Offset ###
Sometime we need to get not one but not all users. For the first, try to get first 2 users from the table.
```lua
users = User.get:limit(2):all()
print("We get " .. users:count() .. " users")
-- We get 2 users
print("Second user name is: " .. users[2].username)
-- Second user name is: Second user
```Great! But if we want to get next two users? We can do this by using following example:
```lua
users = User.get:limit(2):offset(2):all()
print("Second user name is: " .. users[2].username)
-- Second user name is: New user
```### Order result ###
Also you can sort your result by order. We want to sort users from the oldest to the youngest.
```lua
users = User.get:order_by({desc('age')}):all()
print("First user id: " .. users[1].id)
-- First user id: 3
```But we have 2 users with age 44. We can order them by name.
```lua
users = User.get:order_by({desc('age'), asc('username')}):all()
```
You can order your table query by other parameters too.### Group result ###
And now try to group your results:
```lua
users = User.get:group_by({'age'}):all()
print('Find ' .. users:count() ..' users')
-- Find 4 users
```### Where and Having ###
These two methods have the same syntax. But `having` you can use only with `group_by `method. There's one simple example:
```lua
user = User.get:where({username = "First user"}):first()
print("User id is: " .. user.id) -- User id is: 1
```And the same for `having`:
```lua
users = User.get:group_by({'id'}):having({age = 44}):all()
print("We get " .. users:count() .. " users with age 44")
-- We get 2 users with age 44
```Great! But what if we need to do more operations than just a differentiation of table fields. We can do that! This is the list with some rules:
*For example we use for default `colname`. It can be any column in your model*
1. `colname = value` - the same as `colname = value`
2. `colname__lt = value` - the same as `colname < value` *(`value` must be a number)*
3. `colname__lte = value` - the same as `colname <= value` *(`value` must be a number)*
4. `colname__gt = value` - the same as `colname > value` *(`value` must be a number)*
5. `colname__gte = value` - the same as `colname >= value` *(`value` must be a number)*
6. `colname__in = {v1, v2,...,vn}` - the same as `colname in (value1, value2,...,vn)` *(`vn` can be number, string)*
7. `colname__notin = {v1, v2,...,vn}` - the same as `colname not in (value1, value2,...,vn)` *(`vn` can be number, string)*
8. `colname__null = value` - if value is `true` then result is `colname is NULL`, but if value is `false` then result is `colname is not NULL`
### Super SELECT ###But if we do ...
```lua
user = User.get:where({age__lt = 30,
age__lte = 30,
age__gt = 10,
age__gte = 10
})
:order_by({asc('id')})
:group_by({'age', 'password'})
:having({id__in = {1, 3, 5},
id__notin = {2, 4, 6},
username__null = false
})
:limit(2)
:offset(1)
:all()
```This example doesn't make sense. But it works!
### JOIN TABLES ###
Now we can create a join of tables. But before that we create some table with `foreign key` column:
```lua
local News = Table({
__tablename__ = "group",
title = fields.CharField({max_length = 100, unique = false, null = false}),
text = fields.TextField({null = true}),
create_user_id = fields.ForeignKey({to = User})
})
```And add two test news:
```lua
local user = User.get:first()local news = News({title = "Some news", create_user_id = user.id})
news:save()news = News({title = "Other title", create_user_id = user.id})
news:save()
```Now try to get all the news from the owner.
```lua
local news = News.get:join(User):all()
print("First news user id is: " .. news[1].user.id) -- First news user id is: 1
```But if we want to get all users and also to get three news for each user . We can do this by following example:
```lua
local user = User.get:join(News):first()
print("User " .. user.id .. " has " .. user.news_all:count() .. " news")
-- User 1 has 2 news
for _, user_news in pairs(user.news_all) do
print(user_news.title)
end
-- Some news
-- Other title
```If you want to get all the values from tables you can combine table's names and prefix "_all". Like in previous example
```lua
user.news_all
````news_all` - returns a list of all news for current user or `nil` if news does not exist.
## Create column types ##
We can create a field type for every table. Try to create EmailField type:
```lua
fields.EmailField = fields:register({
__type__ = "varchar",
settings = {
max_length = 100
},
validator = function (value)
return value:match("[A-Za-z0-9%.%%%+%-]+@[A-Za-z0-9%.%%%+%-]+%.%w%w%w?%w?")
end,
to_type = function (value)
return value
end,
as = function (value)
return "'" .. value .. "'"
end
})
```Let's make it step by step:
`__type__` - this variable creates the appropriate type in the database (`"varchar"`, `"integer"`, `"boolean"`, `"date"`, `"datetime"`, `"text"`, ...).
By default this value is `"varchar"`.`settings` -set a field value as default (*fields settings was describe later*). By default this value is empty.
`validator` - validates the value of the variable. If value is correct - returns `true`. If value is not correct it returns `false` and doesn't update or add rows. By default it always returns `true`.
`to_type` - parses value for correct sql save. By default it is not parsed value
`as` - returns the value from lua to SQL. By default it is not parsed value.
```lua
local UserEmails = Table({
__tablename__ = "user_emails",
email = fields.EmailField(),
user_id = fields.ForeignKey({ to = User })
})local user_email = UserEmails({
email = "mailexample.com",
user_id = user.id
})
user_email:save() -- This email wasn't added!-- And try again
local user_email = UserEmails({
email = "[email protected]",
user_id = user.id
})
user_email:save() -- This email was added!
user_email.email = "not email"
user_email:save() -- This email wasn't updated
user_email.email = "[email protected]"
user_email:save() -- This email was updated
```## Final ##
All code you can see in example.lua file. Feel free to use it! Good luck!