8d4212a12cc4e31ab7c631d431afcc8c87f9ca40
[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 QString columnsForSelect(const QString& prefix = QString());
47     static QString selectQuery();
48     static QString updateQuery();
49     static QSqlRecord toRecord(const QList<QSqlField> & columnList);
50
51     static QVariant convertToC(QVariant value, QVariant::Type colType);
52     static QVariant convertToDb(QVariant value, QVariant::Type colType);
53 };
54
55 template <typename T>
56 OrmRecord<T>::OrmRecord()
57 {
58     QSqlRecord::operator=(T::sColumns);
59 }
60
61 template <typename T>
62 T OrmRecord<T>::hydrate(const QSqlRecord& record)
63 {
64     T object;
65     object.QSqlRecord::operator=(record);
66     return object;
67 }
68
69 // updates specified column 'col'
70 // if the value is not specified  as an argument,
71 // it's taken from the reford itself
72 // see also: setValue() method for more details
73 template <typename T>
74 void OrmRecord<T>::update(QString col, QVariant value)
75 {
76     QSqlQuery query;
77     query.prepare(QString(updateQuery() + "SET %1 = :col WHERE id = :id").arg(col));
78     if(value.isValid()) // take 'col' value from the method's arguments
79         query.bindValue(":col", value);
80     else // take 'col' value from the record; see setValue()
81         query.bindValue(":col", convertToDb(this->value(col), this->value(col).type()));
82     query.bindValue(":id", this->value("id"));
83     //query.bindValue(":id", convertToDb(value("id"), QVariant::Int));
84     query.exec();
85 }
86
87 template <typename T>
88 QVariant OrmRecord<T>::value(QString col) const
89 {
90     return convertToC(QSqlRecord::value(col), T::sColumns.field(col).type());
91 }
92
93 template <typename T>
94 void OrmRecord<T>::setValue(QString col, QVariant value)
95 {
96     QSqlRecord::setValue(col, convertToDb(value, T::sColumns.field(col).type()));
97 }
98
99 template <typename T>
100 T OrmRecord<T>::loadOne(QSqlQuery query)
101 {
102     if (!query.isActive())
103     {
104         if (!query.exec())
105         {
106             throw new OrmSqlException(query.lastError().text());
107         }
108     }
109
110     if (!query.next())
111     {
112         throw new OrmNoObjectException();
113     }
114
115     return hydrate(query.record());
116 }
117
118 template <typename T>
119 QList<T> OrmRecord<T>::load(QSqlQuery query)
120 {
121     if (!query.isActive())
122     {
123         if (!query.exec())
124         {
125             throw new OrmSqlException(query.lastError().text());
126         }
127     }
128
129     QList<T> objects;
130     while (query.next())
131     {
132         objects << hydrate(query.record());
133     }
134
135     return objects;
136 }
137
138 template <typename T>
139 QString OrmRecord<T>::columnsForSelect(const QString& prefix)
140 {
141     QStringList prefixedColumns;
142     for (int i=0; i<T::sColumns.count(); i++)
143     {
144         prefixedColumns.append(prefix.isEmpty() ?
145             T::sColumns.field(i).name() :
146             QString("%1.%2").arg(prefix, T::sColumns.field(i).name()));
147     }
148     return prefixedColumns.join(",");
149 }
150
151 template <typename T>
152 QString OrmRecord<T>::selectQuery()
153 {
154     return QString("SELECT %1 FROM %2 ").arg(columnsForSelect(), T::sTableName);
155 }
156
157 template <typename T>
158 QString OrmRecord<T>::updateQuery()
159 {
160     return QString("UPDATE %1 ").arg(T::sTableName);
161 }
162
163 template <typename T>
164 QSqlRecord OrmRecord<T>::toRecord(const QList<QSqlField> & columnList)
165 {
166     QSqlRecord record;
167     foreach (const QSqlField & col, columnList)
168     {
169         record.append(col);
170     }
171     return record;
172 }
173
174 template <typename T>
175 QVariant OrmRecord<T>::convertToC(QVariant value, QVariant::Type colType)
176 {
177     if (colType == QVariant::DateTime && value.canConvert<uint>())
178     {
179         QDateTime date;
180         date.setTimeSpec(Qt::UTC);
181         date.setTime_t(value.toUInt());
182         return date;
183     }
184
185     return value;
186 }
187
188 template <typename T>
189 QVariant OrmRecord<T>::convertToDb(QVariant value, QVariant::Type colType)
190 {
191     if (colType == QVariant::DateTime && value.canConvert<QDateTime>())
192     {
193         return value.toDateTime().toTime_t();
194     }
195
196     return value;
197 }
198
199 #endif // ORMRECORD_H