//----------------------------------------------------------------------------//
// dbstep_t3.cpp - This example shows a more advanced object/relational
// mapping between classes NewsRecord (table News) and NewsAuthor
// (table Authors).
// A simple join is made between tables and the result is mapped onto
// class NewsRecord.
//----------------------------------------------------------------------------//

#include <iostream>

#ifdef __BORLANDC__
#include <conio.h>
#endif

#include "dbstep_test.h"

// this class is mapped with table 'News' in database
// and must be derived from DBSTEP::Record

class NewsRecord: public DBSTEP::Record
{
    // output operator
    friend std::ostream& operator << (std::ostream& os, const NewsRecord& nr);

protected:

    // virtual method to map fields
    virtual void addFields();

    int id;
    int author_id;
    DBSTEP::Datetime newsdate;
    std::string title;
    std::string summary;

    // these fields are filleb by method joinSelect
    std::string name;
    std::string email;

public:
    NewsRecord(DBSTEP::Db* db_ptr);
    virtual ~NewsRecord();

    // method for setting fields
    void setFields(int p_author_id,
                   DBSTEP::Datetime p_newsdate, std::string title,
                   std::string summary);

    // selects that joins table News and table Authors
    bool joinSelect();
};

// method for setting fields
void NewsRecord::setFields(int p_author_id,
                           DBSTEP::Datetime p_newsdate, std::string p_title,
                           std::string p_summary)
{
    id=0;  // id is auto-increment
    author_id=p_author_id;
    newsdate = p_newsdate;
    title=p_title;
    summary=p_summary;
}


// output operator
std::ostream& operator << (std::ostream& os, const NewsRecord& nr)
{
    os << "Title: " << nr.title << " ("
    << nr.newsdate.getDay() << "/"
    << nr.newsdate.getMonth() << "/"
    << nr.newsdate.getYear()
    << ") Author:"
    << nr.name << " ("
    << nr.email << ")";

    return os;
}

// the constructor sets table name and calls mapping method 'addFiels()'

NewsRecord::NewsRecord(DBSTEP::Db* db_ptr):
        DBSTEP::Record(db_ptr, "News")
{
    addFields();
}

NewsRecord::~NewsRecord()
{ }


// this method maps fields and class variables


void NewsRecord::addFields()
{
    addField("id", id, DBSTEP_ALLOP_FIELD | DBSTEP_PRIMARY_KEY | DBSTEP_AUTOINC_FIELD);
    addField("author_id", author_id);
    addField("newsdate", newsdate);
    addField("title", title);
    addField("summary", summary);

    // these fields are read-only and are filled with a SQL join
    // with Authors table

    addField("name", name, DBSTEP_SELECT_FIELD);
    addField("email", email, DBSTEP_SELECT_FIELD);


}

bool NewsRecord::joinSelect()
{
    DBSTEP::Query q(db_ptr);
    // we use 'AS' for each field to make sqlite happy

    q = "SELECT n.id AS id, n.newsdate AS newsdate, n.title AS title, \
        n.summary AS summary, \
        a.email AS email, a.name AS name \
        FROM News n, Authors a \
        WHERE a.id = n.author_id \
        ORDER BY n.id";

    return select(&q);

}

/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////

// this class is mapped with table 'Authors' in database
// and must be derived from DBSTEP::Record

class AuthorRecord: public DBSTEP::Record
{
protected:

    // virtual method to map fields
    virtual void addFields();

    int id;
    std::string name;
    std::string email;

public:
    AuthorRecord(DBSTEP::Db* db_ptr);
    virtual ~AuthorRecord();

    // method for setting fields
    void setFields(int p_id, std::string p_name,
                   std::string p_email);

};

// the constructor sets table name and calls mappin method 'addFiels()'

AuthorRecord::AuthorRecord(DBSTEP::Db* db_ptr):
        DBSTEP::Record(db_ptr, "Authors")
{
    addFields();
}

AuthorRecord::~AuthorRecord()
{ }


void AuthorRecord::setFields(int p_id, std::string p_name,
                             std::string p_email)
{
    id=p_id;
    name=p_name;
    email = p_email;
}


// this method maps fields and class variables

void AuthorRecord::addFields()
{
    addField("id", id, DBSTEP_ALLOP_FIELD | DBSTEP_PRIMARY_KEY);
    addField("name", name);
    addField("email", email);
}

/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////

int main (int argc, char** argv)
{
    // here we get driver code
    int drv=getDriver(argc, argv);

    // init parameter map
    DBSTEP::StringMapType param_map;

    try
    {
        if (drv == DBSTEP_FBIRD) 
	  param_map ["dbname"] = "dbstep_test.gdb";
        else
	{
	  param_map ["dbname"] = "dbstep_test";
          param_map ["host"] = "localhost";
          param_map ["user"] = "dbstep_user";
          param_map ["password"] = "dbstep_test";
        }

          param_map ["autocommit"] = "0";

        // new database object
        DBSTEP::Db db(drv);

        // opens connection
        db.openConnection(param_map);
        {
            AuthorRecord a(&db);
            a.setQueryMode(DBSTEP_QUERY_EXEC_PREPARED);

            a.setFields(1, "Pippo", "pippo@foo.bar");
            a.insert();

            a.setFields(2, "Pluto", "pluto@foo.bar");
            a.insert();

            NewsRecord n(&db);
            NewsRecord n2(&db);
            n.setQueryMode(DBSTEP_QUERY_EXEC_PREPARED);
            n2.setQueryMode(DBSTEP_QUERY_EXEC_PREPARED);

            n.setFields(2, DBSTEP::Datetime(2001, 3, 6, 12, 12, 11),
                        "Title1", "Summary1");
            n.insert();

            n.setFields(1, DBSTEP::Datetime(2001, 11, 6, 0, 0, 0),
                        "Title2", "Summary2");
            n.insert();

            n.setFields(1, DBSTEP::Datetime(2001, 1, 5, 0, 0, 0),
                        "Title3", "Summary3");
            n.insert();

            std::cout << "\nJoin SELECT\n";
            n.joinSelect();
            n2.joinSelect();
	    
            while (n.next())
            {
             while (n2.next())
                std::cout << n2 << std::endl;
                std::cout << n << std::endl;
            }
            // cleanup
            n.removeAll();
            a.removeAll();
            std::cout.flush();
        }

        db.closeConnection();
    }
    catch (errException e)
    {
        std::cout << "\nERROR: " << DBSTEP::Params::instance()->getErrManager()->first()->getName() <<  std::endl;
    }
    
    // cleans up
    DBSTEP::Params::instance()->dispose();

#ifdef __BORLANDC__
    getch();
#endif

    return 0;

}


syntax highlighted by Code2HTML, v. 0.9.1