combined EVENT and VIRTUAL_EVENT => 'EVENT' now
[toast/confclerk.git] / src / orm / ormrecord.h
1 #ifndef ORMRECORD_H
2 #define ORMRECORD_H
3
4 #include <QSqlQuery>
5 #include <QSqlRecord>
6 #include <QSqlField>
7 #include <QSqlError>
8 #include <QStringList>
9 #include <QDateTime>
10 #include <QDebug>
11
12 class OrmException
13 {
14 };
15
16 class OrmNoObjectException : OrmException
17 {
18 };
19
20 class OrmSqlException : OrmException
21 {
22 public:
23     OrmSqlException(const QString& text) : mText(text) {}
24     QString text() const { return mText; }
25
26 private:
27     QString mText;
28 };
29
30 template <typename T>
31 class OrmRecord : protected QSqlRecord
32 {
33 public:
34     OrmRecord();
35     static T hydrate(const QSqlRecord& record);
36     void update(QString col, QVariant value = QVariant()); // updates specified column 'col'
37
38 protected:
39     QVariant value(QString col) const;
40     void setValue(QString col, QVariant value);
41
42     static T loadOne(QSqlQuery query);
43     static QList<T> load(QSqlQuery query);
44
45     // auxiliary methods
46     static QSqlRecord toRecord(const QList<QSqlField> & columnList);
47     // all record items/columns are in one table
48     static QString columnsForSelect(const QString& prefix = QString());
49     static QString selectQuery();
50     static QString updateQuery();
51
52     static QVariant convertToC(QVariant value, QVariant::Type colType);
53     static QVariant convertToDb(QVariant value, QVariant::Type colType);
54 };
55
56 template <typename T>
57 OrmRecord<T>::OrmRecord()
58 {
59     QSqlRecord::operator=(T::sColumns);
60 }
61
62 template <typename T>
63 T OrmRecord<T>::hydrate(const QSqlRecord& record)
64 {
65     T object;
66     object.QSqlRecord::operator=(record);
67     return object;
68 }
69
70 // updates specified column 'col'
71 // if the value is not specified  as an argument,
72 // it's taken from the reford itself
73 // see also: setValue() method for more details
74 template <typename T>
75 void OrmRecord<T>::update(QString col, QVariant value)
76 {
77     QSqlQuery query;
78     query.prepare(QString(updateQuery() + "SET %1 = :col WHERE id = :id").arg(col));
79     if(value.isValid()) // take 'col' value from the method's arguments
80         query.bindValue(":col", value);
81     else // take 'col' value from the record; see setValue()
82         query.bindValue(":col", convertToDb(this->value(col), this->value(col).type()));
83     query.bindValue(":id", this->value("id"));
84     //query.bindValue(":id", convertToDb(value("id"), QVariant::Int));
85     query.exec();
86 }
87
88 template <typename T>
89 QVariant OrmRecord<T>::value(QString col) const
90 {
91     return convertToC(QSqlRecord::value(col), T::sColumns.field(col).type());
92 }
93
94 template <typename T>
95 void OrmRecord<T>::setValue(QString col, QVariant value)
96 {
97     QSqlRecord::setValue(col, convertToDb(value, T::sColumns.field(col).type()));
98 }
99
100 template <typename T>
101 T OrmRecord<T>::loadOne(QSqlQuery query)
102 {
103     if (!query.isActive())
104     {
105         if (!query.exec())
106         {
107             throw OrmSqlException(query.lastError().text());
108         }
109     }
110
111     if (!query.next())
112     {
113         throw OrmNoObjectException();
114     }
115
116     return hydrate(query.record());
117 }
118
119 template <typename T>
120 QList<T> OrmRecord<T>::load(QSqlQuery query)
121 {
122     if (!query.isActive())
123     {
124         if (!query.exec())
125         {
126             qDebug() << "Error: " << query.lastError().driverText() << "; Type: " << query.lastError().type();
127             throw OrmSqlException(query.lastError().text());
128         }
129         else
130         {
131             qDebug() << "SQL OK";
132         }
133     }
134
135     QList<T> objects;
136     while (query.next())
137     {
138         objects << hydrate(query.record());
139     }
140     qDebug() << "Fetch done";
141     return objects;
142 }
143
144 template <typename T>
145 QString OrmRecord<T>::columnsForSelect(const QString& prefix)
146 {
147     QStringList prefixedColumns;
148     for (int i=0; i<T::sColumns.count(); i++)
149     {
150         prefixedColumns.append(prefix.isEmpty() ?
151             T::sColumns.field(i).name() :
152             QString("%1.%2").arg(prefix, T::sColumns.field(i).name()));
153     }
154     return prefixedColumns.join(",");
155 }
156
157 template <typename T>
158 QString OrmRecord<T>::selectQuery()
159 {
160     return QString("SELECT %1 FROM %2 ").arg(columnsForSelect(), T::sTableName);
161 }
162
163 template <typename T>
164 QString OrmRecord<T>::updateQuery()
165 {
166     return QString("UPDATE %1 ").arg(T::sTableName);
167 }
168
169 template <typename T>
170 QSqlRecord OrmRecord<T>::toRecord(const QList<QSqlField> & columnList)
171 {
172     QSqlRecord record;
173     foreach (const QSqlField & col, columnList)
174     {
175         record.append(col);
176     }
177     return record;
178 }
179
180 template <typename T>
181 QVariant OrmRecord<T>::convertToC(QVariant value, QVariant::Type colType)
182 {
183     if (colType == QVariant::DateTime && value.canConvert<uint>())
184     {
185         QDateTime date;
186         date.setTimeSpec(Qt::UTC);
187         date.setTime_t(value.toUInt());
188         return date;
189     }
190
191     return value;
192 }
193
194 template <typename T>
195 QVariant OrmRecord<T>::convertToDb(QVariant value, QVariant::Type colType)
196 {
197     if (colType == QVariant::DateTime && value.canConvert<QDateTime>())
198     {
199         return value.toDateTime().toTime_t();
200     }
201
202     return value;
203 }
204
205 #endif // ORMRECORD_H
206