Let's create an TreeFrog application.
Try creating a simple blog system which can be added / edited / deleted texts.
Creating a skeleton of application
First, try creating a skeleton of "blogapp" application, which includes the directory tree and setting files. Enter that from command-line. In Windows, enter on TreeFrog Command Prompt.
|
|
Creating a table
Create a table called blog in the database, which includes title and body fields. Next examples are MySQL and SQLite.
MySQL :
Please check the character-set information set to the database, and setup MySQL tools path beforehand.
|
$ mysql -u root -p mysql> CREATE DATABASE blogdb DEFAULT CHARACTER SET utf8; mysql> USE blogdb; mysql> CREATE TABLE blog (id INTEGER AUTO_INCREMENT PRIMARY KEY, title VARCHAR(20), body VARCHAR(200), created_at TIMESTAMP DEFAULT 0, updated_at TIMESTAMP DEFAULT 0, lock_revision INTEGER) DEFAULT CHARSET=utf8; Query OK, 0 rows affected (0.02 sec) mysql> DESC blog; mysql> quit |
SQLite :
Place the database file in the db directory.
$ cd blogappCREATE TABLE blog (id INTEGER PRIMARY KEY, title VARCHAR(20), body VARCHAR(200), created_at TIMESTAMP, updated_at TIMESTAMP, lock_revision INTEGER); sqlite> .quit |
So, the blog table with the fields of id, title, body, created_at, updated_at and lock_revision was created.
If with the field(s) of created_at or updated_at, TreeFrog automatically inserts creation date/time and update date/time, respectively. And, create the lock_revision field as integer type to lock the record optimistic.
Optimistic Lock
Mechanism to update a record safely without locking. When updating a record, the system checks whether others have changed it or not. If changed already, the system cancels the update process itself.
Setting database information
Set the database information to config/database.ini file. Open the file by editor program, and enter proper values into the items of dev section.
Example of MySQL :
[dev] |
Example of SQLite :
[dev] |
To check whether the settings is correct, try to access to the database and show available tables.
| $ cd blogapp $ tspawn --show-tables DriverType: QSQLITE DatabaseName: db\blogdb HostName: Database opened successfully ----- Available tables: blog |
If this, it's OK.
If no database driver, displays an error message like that:
"QSqlDatabase: QMYSQL driver not loaded"
In this case, download database drivers in Download Page and install it referring the install file.
To check database drivers installed, execute the following command.
| $ tspawn --show-drivers Available database drivers for Qt: QSQLITE QMYSQL3 QMYSQL QODBC3 QODBC |
Setting the template system
In TreeFrog Framework, you can choose either the ERB or Otama as the template system. Set to the development.ini file.
| TemplateSystem=ERB or TemplateSystem=Otama |
Auto generating source codes from the table
Run the generator command, tspawn, from command-line to generate source codes of its base. The following is generating a controller, a model and a view. The parameter is the table name, lower-case.
|
|
You can change to generate only a controller or only a model by the option of tspawn.
If the SQL driver doesn't exist in Qt SDK, the command would be error. In this case, build the SQL driver. See FAQ.
Or, you can download the package of SQL database drivers from this page, and install it.
SQLite driver is built in Qt SDK always, therefore it might be better to use, if you just try a little.
Building source codes
Only once run the following command to generate a Makefile.
| $ qmake -r "CONFIG+=debug" |
Run make command in app directory to compile all of controllers, models, views and helpers.
| $ make (On Windows, run 'mingw32-make' command instead) |
When the build succeeds, four shared libraries will be generated in lib directory.
On Windows, shared libraries of debug mode will be generated as default. If you make libraries of release mode, execute a next command. See Qt doc for details of CONFIG variable.
| $ qmake -r "CONFIG+=release" |
Starting up the application server
Start up the application server in the root directory of blogapp. The server will be proceed assuming the current directory as the application root.
Press Ctrl+c button to stop the server.
| $ treefrog -e dev |
On Windows, use treefrogd command.
| > treefrogd -e dev |
On Windows, use treefrogd command if the Web application is built in debug mode. Use treefrog command if built in release mode. Mixing objects release and debug modes, it does not work properly.
Specify -d option to execute the server in background:
| $ treefrog -d -e dev |
Stop command:
| $ treefrog -k stop |
Forced termination command:
| $ treefrog -k abort |
Restart command:
| $ treefrog -k restart |
If the firewall is enabled, open the port (default:8800).
Access by web browser
Open the URL, http://localhost:8800/Blog/index, by web browser. The following screen will be displayed (like Rails..). First, no entry.
Two entries was registered. You can register, edit, delete.
No problem showing multi-bytes characters, Japanese.
It's easy!
TreeFrog has the URL routing system as of other frameworks, which is mechanism of calling the corresponding controller's method (action) from the requested URL.
The developed source codes can work on other platform, if compile again.
Published this sample web application. Access here!
Fast just like a desktop application. Use it freely.
Controller source code
Try checking out the source codes of the generated controller.
Header file, first. The actions equivalent to CRUD are defined. Don't mind magic codes, here.
|
class T_CONTROLLER_EXPORT BlogController : public ApplicationController public slots: T_DECLARE_CONTROLLER(BlogController, blogcontroller) // magic code |
Next, source file.
100 lines, so go together.
|
BlogController::BlogController(const BlogController &) void BlogController::index() void BlogController::show(const QString &pk) void BlogController::entry() void BlogController::create() void BlogController::edit(const QString &pk) void BlogController::save(const QString &pk) QString error; void BlogController::remove(const QString &pk) // Don't remove below this line |
- The lock-revision exists for Optimistic Lock.
As you see, use the texport method to pass values to the view. The parameter of this method is a object of QVariant class, which can be any object such as int, QString, QList, QHash and etc. See details of Qt document.
Mechanism of View
Two template systems, ERB and original system named Otama, are adopted in TreeFrog framework.
Generator command generates view files of ERB as default. In ERB, write C++ code into the part of "<% .. %>". If render() method is called in index action, the system returns the contents of index.erb as a response.
| <!DOCTYPE HTML> <%#include "blog.h" %> <html> <head> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <title><%= controller()->name() + ": " + controller()->activeAction() %></title> </head> <body> <h1>Listing Blog</h1> <%== linkTo("New entry", urla("entry")) %><br /> <br /> <table border="1" cellpadding="5" style="border: 1px #d0d0d0 solid; border-collapse: collapse;"> <tr> <th>ID</th> <th>Title</th> <th>Body</th> </tr> <% tfetch(QList<Blog>, blogList); %> <% for (QListIterator<Blog> it(blogList); it.hasNext(); ) { const Blog &i = it.next(); %> <tr> <td><%= i.id() %></td> <td><%= i.title() %></td> <td><%= i.body() %></td> <td> <%== linkTo("Show", urla("show", i.id())) %> <%== linkTo("Edit", urla("edit", i.id())) %> <%== linkTo("Remove", urla("remove", i.id()), Tf::Post, "confirm('Are you sure?')") %> </td> </tr> <% } %> </table> |
The next is Otama template system.
Otama template system is entirely separate templates and presentation logic. Templates are written in perfect HTML. Mark the element (start-tag) of the part that you want to re-write dynamically, and write down logic (C++ code) associated with the mark to the presentation logic file. Therefore, it is physically possible to divide the labor between designers and programmers.
Look into index.html file generated by the command.
If render() method is called in index action, the system returns the contents of index.html as a response. As you see, this file conforms to HTML version 5. If open by the today's web browser, the design doesn't break.
| <!DOCTYPE HTML> <html> <head> <meta http-equiv="content-type" content="text/html;charset=UTF-8" /> <title data-tf="@head_title"></title> </head> <body> <h1>Listing Blog</h1> <a href="#" data-tf="@link_to_entry">New entry</a><br /> <br /> <table border="1" cellpadding="5" style="border: 1px #d0d0d0 solid; border-collapse: collapse;"> <tr> <th>ID</th> <th>Title</th> <th>Body</th> <th></th> </tr> <tr data-tf="@for"> ← mark as @for <td data-tf="@id"></td> <td data-tf="@title"></td> <td data-tf="@body"></td> <td> <a data-tf="@linkToShow">Show</a> <a data-tf="@linkToEdit">Edit</a> <a data-tf="@linkToRemove">Remove</a> </td> </tr> </table> </body> </html> |
Then, look into index.olg corresponding to the presentation logic. This file associates above marks with C++ logic, but #include is reserved word. A part from the mark to the empty line is one set. Logic parts are codes of C++ language almost. The association sets include operators such as ~= or :==, by which the behavior changes.It uses Custem Data Attibute of HTML5 named data-tf to mark elements each. The string starting with "@" is the mark of Otama.
| #include "blog.h" /* just C++ code, include the blog.h */
@head_title ~= controller()->controllerName() + ": " + controller()->actionName() @for : @id ~= i.id() /* assigns the results of i.id() to the content of the element marked with @id */ @title ~= i.title() @body ~= i.body() @linkToShow :== linkTo("Show", urla("show", i.id())) /* replaces the element and child elements with the results of linkTo() */ @linkToEdit :== linkTo("Edit", urla("edit", i.id())) @linkToRemove :== linkTo("Remove", urla("remove", i.id()), Tf::Post, "confirm('Are you sure?')") @linkToEntry :== linkTo("New entry", urla("entry")) |
Briefly describes Otama operator.
~ (tilde) operator sets the result of the right side to the content of the marked element.
= (equals sign) operator converts to HTML entities, that is HTML-escape. Therefore, ~= operator converts the result of the right side to HTML entities and sets to the content of the marked element. If you don't want HTML-escape, use ~== operator instead.
Moreover, : (colon) operator replaces the element and child elements with the result of the right side. Therefore, :== operator replaces it with the raw value, not converted.
Passing data from Controller to View
If the data (object) texport-ed in controllers used in the view, it must be declared by tfetch method (Actually, macro). The arguments are the variable type and the variable name. Then, you can use it as a normal variable, that is the exactly value texport-ed in the controller. See above presentation logic file.
Example :
|
Controller side : |
Otama system generates C++ codes from these template files and presentation logic. Internally, tmake command is processes it. The codes are compiled to a shared library of view. Therefore, it can work very fast.
HTML Glossary:
An Element consists of a start-tag, a content and a end-tag. For example, in case of "<p>Hello</p>" element, <p> is the start-tag, Hello is the content, </p> is the end-tag.
Model and ORM
TreeFrog framework has the concept Model includes ORM object, that is has-a relationship. Most other frameworks have the concept that Model is equals to ORM object as default, which is different from that of TreeFrog. That said, its ability is same. Here, understand that Model is small wrapper of ORM object.
O/R mapping module (O/R mapper) named SqlObject is built in TreeFrog as default. Check the generated SqlObject file, blogobject.h
Because C++ is a statically typed language, type declarations are required. Fields in the table are declared as public member variables. It's almost a struct. Only in this way, you can use methods equivalent to CURD, create, update, findFirst and remove. These methods are defined in TSqlObject class and TSqlORMapper class.
| class T_MODEL_EXPORT BlogObject : public TSqlObject, public QSharedData { public: int id; QString title; QString body; QDateTime created_at; QDateTime updated_at; int lock_revision; enum PropertyIndex { Id = 0, Title, Body, CreatedAt, UpdatedAt, LockRevision, }; int primaryKeyIndex() const { return Id; } /*** Don't modify below this line ***/ |
Although O/R mapper includes methods of inquiry or update by primary key, SqlObject can have only one primary key as a return value of primaryKeyIndex method. Therefore, in the table with multiple primary keys, please modify this value if necessary. By using TCriteria class, it is possible to inquiry by more complex conditions. See the section.
Then, let's look into the Model header file.
Setter/getter of each property and static methods of object creation and getter are defined. Because methods of save and remove are defined in TAbstractModel class that is the parent, Blog class has methods equivalent to CURD, create, get, save and remove.
| class T_MODEL_EXPORT Blog : public TAbstractModel { public: Blog(); Blog(const Blog &other); Blog(const BlogObject &object); // constructor made from ORM object ~Blog(); int id() const; // Setters and getters, from here void setId(int id); QString title() const; void setTitle(const QString &title); QString body() const; void setBody(const QString &body); QDateTime createdAt() const; QDateTime updatedAt() const; int lockRevision() const; static Blog create(int id, const QString &title, const QString &body); // object creation static Blog create(const QHash<QString, QString> &values); // object creation from Hash static Blog get(int id); // Gets object specified by ID static Blog get(int id, int lockRevision); static QList<Blog> getAll(); // Gets all objects private: TSqlObject *data(); Q_DECLARE_METATYPE(Blog) // magic code, from here |
Although codes automatically generated by the generator are not so many, basic functions are working. Of course, the generated codes are not perfect, and since the actual application process is more complex, it might not be enough. Modifying would be necessary. Understand that generator command is the tool to save effort just a bit. In the background of codes described above, various functions are working such as cookie tampering checking, optimistic locking, SQL injection protection, CSRF protection with authentication token, etc.
If interested, take a look to the source codes.
Create Blog App Demo





