
Database
========
The database component of the application is very important, because
most of the web applications are based on relational databases.
It allows an application to interact with a database, to get and
display data from it, to save, update and delete data in it, etc.
The framework tries to make this interaction as easy and as convenient
as possible for the application developpers, without loosing the
flexibility. It also tries to ensure that the database is used in 
abstract way, so that it doesn't matter for the programer whether 
the database is MySQL, Oracle, etc.


How to enable the DB component
-------------------------------
To enable the DB component, open the file 'config/const.Settings.php'
and set the constant 'USES_DB' as 'true'. After this, create the
file 'config/const.DB.php' and define in it these constants:

define("DBHOST", "DB server name");
define("DBUSER", "username");
define("DBPASS", "password");
define("DBNAME", "name of the database");

These will be used by the framework to open a connection with the
database, and this connection will be used by default for all the
interactions of the application with the database.


How it is used in templates
----------------------------
Inside a template, we can get and display data directly from the DB
using the tags <Recordset> and <Repeat>. The element <Recordset>
defines a recordset, which has a query and an ID. The <Repeat> element
refers to a recordset by its ID, and the body of the <Repeat> is
repeated for each record in the recordset. The fields of the recordset
are used as {{#variables}} inside the body of the <Repeat>. When the
framework processes this template, it gets the recordset of the
template from its ID, opens it (executes its query and gets its 
result), then for each record of the recordset it repeats the body
of the <Repeat>, by defining new variables for each field of the 
record before each repetition. For more details about declaring
<Recordset> and <Repeat> elements look at the file 'templates.txt' .

<Recordset> elements can be declared inside the template, however 
it is recomended to declare them in a separate file for each template
(with the extension *.rs or *.tpc) and to include it in the template
like this: <Include SRC="{{#./}}tpl_name.rs"/>. This has the benefit
that the template designer doesn't have to mess with queries and
recordsets, and the database designer has all the queries in separate
files and doesn't have to search through the templates.

When we have a page recordset (see 'templates.txt' for more details),
not all the records of the query are retrived from the database, but
only one page. Which page is actualy retrieved is determined by 
the state variable 'rsId->current_page' (current page of recordset
with ID rsId).


How it is used in PHP
----------------------
In the PHP code we can interact with the database using these 
functions:

1 - WebApp::openRS("rs_id)
    "rs_id" is the ID of a recordset declared by a tag like this:
        <Recordset ID="rs_id">
            <Query> . . . </Query>
        </Recordset>
    This function executes the query of the given <Recordset> and
    returns the result as an object of type Recordset (see below).
    The query is usually a "SELECT ..." (something that returns 
    some records).
    
2 - WebApp::execDBCmd("cmd_id")
    "cmd_id" is the ID of a database command declared like this:
        <dbCommand ID="cmd_id">
            <Query> . . . </Query>
        </dbCommand>
    This function executes the query of the given <dbCommand> and
    returns true or false, indicating whether the query was executed
    successfully or not. The query is usually an "insert", "update",
    "delete", etc. (something that does not return records).
    
3 - WebApp::execQuery("query_string")
    "query_string" is a database query whatever. The function returns
    an object of type Recordset if the query is a "select", returns
    nothing otherwise.

The <Recordset> and <dbCommand> elements that are mentioned above
(in the points (1) and (2)) are declared in the DB component of a
webox. This is the file 'boxID.db' in the same folder as the webox
('boxID' is the ID of the webox). For each webox, the framework looks
for the file 'boxID.db' in the same folder as the framework, and if
it exists, it is loaded and processed.

In all the three cases above, the queries may contain {{#variables}}.
These variables are evaluated and replaced just like the other
template variables. They are replaced just before the query is 
executed. If a recordset is opened again, they are replaced again, 
and this time they may have other values and the query may return 
other results.
    

Recordset objects
------------------
Whenever we get some results from the database, the framework always
returns them as an object of type recordset (an instance of the class
Recordset). These objects are quite abstract and are not related to
any type of database in particular. This means that no matter whether
the database is MySQL, Informix or Oracle, we will always program in
the same way. This also means that if an application is currently
based on MySQL and we want to move it on Oracle, we don't have to 
modify anything at all in the code of the application. So, the 
applications built using phpWebApp framework are database independent.

The objects of type Recordset are composed internally of an array of
records and a pointer that points to the current record. Each record
is an associated array which has the fields as keys and the data as
values. Recordsets can be manipulated using the following functions
(suppose that $rs is a Recordset object):
        
* $rs->EOF()
    Returns true if the internal pointer of the Recordset 
    is past the last record.
    
* $rs->BOF()
    Returns true if the internal pointer of the Recordset 
    is before the first record of the recordset.
    
* $rs->MoveNext()
    Moves the internal pointer of the recordset to the next record. 
    If it already is at the EOF, it remains there.
    
* $rs->MovePrev()
    Moves the internal pointer of the recordset to the previous
    record. If it already is at the BOF, it remains there.
    
* $rs->MoveFirst()
    Moves the internal pointer of the recordset to the first record.
    
* $rs->MoveLast()
    Moves the internal pointer of the recordset to the first record.
    
* $rs->Field("FIELD_NAME")
    Returns the value of the field "FIELD_NAME" of the current record 
    (the record that is pointed by the internal pointer).
    If there is no such field in the record, or the internal pointer 
    is at BOF or at EOF, then UNDEFINED is returned.
    
* $rs->Fields()
    Returns the current record as an associated array which has
    as keys the names of the fields and as values the values of 
    the current record.

When a recordset is opened, the pointer is positioned at the first
record. If the recordset is empty (has no records), then EOF() 
returns true.


Using more than one connection
-------------------------------
By default, the framework creates a single connection to the database
and uses it for executing all the queries. It is created like this:
    $cnn = new Connection;
and it gets the parameters of from the file 'config/const.DB.php'.

Sometimes, an application may need more than one connection, e.g. to
connect to more than one database, or to connect as another user 
(with other privileges). In this case we can create the other 
connections at the end of the file 'application.php', like this:

    $cnn1 = new Connection($host, $user, $passwd, $db_name);

Then, we can execute queries using this connection like this:

    WebApp::execQuery($query_str, $cnn1);


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

---------------- Recordsets ------------------------------------
* - Satic Recordsets:  <Recordset rs="rsID" static="true">
    These are recordsets that are opened only once during the
    page construction. Usually the recordsets are opened as
    many times as they are needed (i.e. their query is executed
    and the results retrieved from DB). This has the advantage 
    that the query can be different each time that the recordset
    is opened (depending on the {{variables}} that it contains),
    and this provides for more flexibility and dynamicity.

    However, in some cases, the same query is executed all the
    times that the recordset is opened, e.g. when this recordset
    is used to fill a listbox and this listbox can be 20 times
    in the page. In this case it is more efficent that the
    recordset is opened only once and it is used in the 20 cases.

* - The syntax of tag Recordset now is:
    <Recordset ID="rsID" type="rsType">
    where 'rsType' can be one of the values: "StaticRS", "DynamicRS",
    "EditableRS", "PagedRS", "TableRS". The attibute type can be
    omitted; in this case the default value is "StaticRS".

    A 'static' recordset is the recordset described above, a RS that
    cannot be opened more than once per page. A 'dynamic' RS is a
    recordset that evaluates its query (replaces its {{variables}}),
    executes it and refreshes the content each time that its
    method Open() is called. Notice that before a recordset was
    'dynamic' by default and 'static' only if specified, but now a
    RS is 'static' by default (if no type is given). This is
    because 'static' recordsets are more common in web applications
    and 'dynamic' recordsets are useful only in special cases, 
    e.g. when we have two nested recordsets (associated with two
    nested <Repeat>s) and the inner RS uses a {{variable}} provided
    by the outer RS.

    A 'PagedRS' is a recordset that divides  the results of the
    query into pages of certain size, and retrives from DB only
    one page of them. It requires the attribute 'recs_per_page',
    which specifies the size of the page. It is used to display
    big recordsets, when they cannot fit in a single HTML page.

    An 'EditableRS' is a RS that can be modified from the PHP code.
    The programer can retrive the results of a query from DB and
    then modify them, or start with an empty RS and fill it 
    programatically. It makes possible to use templates for results
    that don't come from DB (e.g. when you want to display a folder
    listing and you get the files and folders using some PHP functions).

    This feature can be used in the "folderListing" webbox, for
    example. Instead of generating all the html code of the 
    folder listing programatically, a <Repeat> template, associated
    with a <Recordset> could be used, and this recordset could be
    filled from an array or from php code (instead of being filled
    from database).
    The benefit of this aproach (vs. generatin all the html code
    in php) would be that we are able to change the layout of the
    'folderListing' webbox more easily, because instead of changing
    the php code, we change only a template.

    The functions of EditableRS are:
    $rs->apply($fun_name)
        //applies the function $fun_name on each record
        //of the recordset; $fun_name is a global function
        //that takes as parameter a reference to an associated 
        //array (a reference to a record), like this:
        // function fun_to_be_applied(&$rec)
    $rs->setFld($fld_name, $fld_value)
        //sets a new value to the given field (in the current record)
    $rs->setFlds($arr_flds)
        //changes some fields of the current recordset
    $rs->addRecs($arr_recs)
    $rs->addRec($rec)
        //adds a new record after the current record,
        //the new record becomes current
    $rs->insRecs($arr_recs)
    $rs->insRec($rec)
        //insert a new record before the current record,
        //the new record becomes the current record
    $rs->rmRec()  //removes the current record
    $rs->rmRecs($nr)
        //removes $nr recs from the recordset, 
        //starting with the current record;
        //if $nr is not given or exeeds the EOF, 
        //then removes all the recs up to the EOF
    $rs->slice($offset, $length)
        //returns a new recordset that contains a slice of this recordset;
        //see the documentation of array_slice of PHP
    $rs->match($condition, $pos)
        //returns true if the query at the given position
        //matches the $condition (usually used by the functions
        //find() and filter() above); if no position is given
        //matches the current record;
        //currently the $condition supports only one field
    $rs->filter($condition)
        //returns a new recordset with the recs that match the
        //given condition, $condition is like the WHERE condition
        //of a query, but it matches using regexp-s, e.g.
        //(username=/^d.*/ AND (firstname=/[^a]+/ OR NOT lastname='Hoxha'))
        //(currently it supports only one field, no AND, OR, (, ) etc.)
    $rs->find($condition)
        //finds the subset of the recordset that matches the
        //condition and positions the pointer to the first
        //record found; $condition is like the condition in filter();
        //if called without parameter, if finds the next one,
        //and so on up to EOF;
    $rs->search($condition)
        //used by find(), finds all the records that match 
        //the condition and stores their indeces in
        //the array $this->found_positions
    $rs->find_next()
        //used by find(), returns the next found record
        //or UNDEFINED if there are no more records
    $rs->getColumn($fld_name)
        //returns an array with the values of the specified field
    $rs->getColumns($fld_names)
        //$fld_names is a comma separated list of field names;
        //returns a new recordset that contains only the specified
        //columns

    A "TableRS" is an editable RS which is associated with a table in
    a DB. After editing this RS, the programer can save the changes
    back to DB. (This is not implemented yet.)

* - WebApp::execQuery($query) now returns an EditableRS.

* - Function: WebApp::openTable($table, $condition) added.
    //opens the given table and returns the records that
    //satisfy the given $condition; returns a TableRS

----------------------------------------------------------------
* - The constant DB_TYPE in 'config/const.Settings.php' specifies
    the type of the DB that the application is using, like this:

    define("DB_TYPE", "MySQL");
        //this constant defines the type of DB that the application uses
        //it can be: "MySQL", "Oracle", "ODBC", "ProgreSQL", etc.
        //(except "MySQL", the others are not implemented yet)

* WebApp::openRS($rs_id, $params =array())
  WebApp::execDBCmd($cmd_id, $params =array())
  
  These functions now take an optional array of parameters, which
  are replaced in the query as {{tpl_vars}}.
--------------------------------------------------------------------
* - PagedRS can also handle the queries which have GROUP BY.

    Along to the state variable 'rs_id->current_page' which keeps
    the current page of the PagedRS, there is the state variable
    'rs_id->recount' which is set to true by the application when
    the records need to to be recounted (e.g. when a new record is
    added in DB, or when a record is deleted).

    Before, the number of the records in a PagedRS was counted only
    when current_page was 1, but this is not very convenient.
--------------------------------------------------------------------
