IxModel.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /****************************************************************************
  2. **
  3. ** https://www.qxorm.com/
  4. ** Copyright (C) 2013 Lionel Marty (contact@qxorm.com)
  5. **
  6. ** This file is part of the QxOrm library
  7. **
  8. ** This software is provided 'as-is', without any express or implied
  9. ** warranty. In no event will the authors be held liable for any
  10. ** damages arising from the use of this software
  11. **
  12. ** Commercial Usage
  13. ** Licensees holding valid commercial QxOrm licenses may use this file in
  14. ** accordance with the commercial license agreement provided with the
  15. ** Software or, alternatively, in accordance with the terms contained in
  16. ** a written agreement between you and Lionel Marty
  17. **
  18. ** GNU General Public License Usage
  19. ** Alternatively, this file may be used under the terms of the GNU
  20. ** General Public License version 3.0 as published by the Free Software
  21. ** Foundation and appearing in the file 'license.gpl3.txt' included in the
  22. ** packaging of this file. Please review the following information to
  23. ** ensure the GNU General Public License version 3.0 requirements will be
  24. ** met : http://www.gnu.org/copyleft/gpl.html
  25. **
  26. ** If you are unsure which license is appropriate for your use, or
  27. ** if you have questions regarding the use of this file, please contact :
  28. ** contact@qxorm.com
  29. **
  30. ****************************************************************************/
  31. #ifndef _IX_MODEL_H_
  32. #define _IX_MODEL_H_
  33. #ifdef _MSC_VER
  34. #pragma once
  35. #endif
  36. /*!
  37. * \file IxModel.h
  38. * \author Lionel Marty
  39. * \ingroup QxModelView
  40. * \brief Interface to manage Qt model/view architecture with classes registered into QxOrm context (Qt widgets and/or QML views)
  41. */
  42. #ifdef _QX_NO_PRECOMPILED_HEADER
  43. #ifndef Q_MOC_RUN
  44. #include <QxPrecompiled.h> // Need to include precompiled header for the generated moc file
  45. #endif // Q_MOC_RUN
  46. #endif // _QX_NO_PRECOMPILED_HEADER
  47. #include <QtCore/qabstractitemmodel.h>
  48. #ifndef Q_MOC_RUN
  49. #include <QxRegister/IxClass.h>
  50. #include <QxDataMember/IxDataMemberX.h>
  51. #include <QxCollection/QxCollection.h>
  52. #include <QxDao/IxSqlRelation.h>
  53. #include <QxDao/QxSqlQuery.h>
  54. #include <QxDao/QxDao.h>
  55. #include <QxValidator/QxInvalidValueX.h>
  56. #include <QxValidator/QxValidatorFct.h>
  57. #endif // Q_MOC_RUN
  58. namespace qx {
  59. namespace model_view {
  60. namespace detail {
  61. template <class T, class M> struct QxNestedModel;
  62. template <class T, class M> struct QxNestedModel_Generic;
  63. template <class T, class M> struct QxNestedModel_Container;
  64. } // namespace detail
  65. } // namespace model_view
  66. } // namespace qx
  67. namespace qx {
  68. /*!
  69. * \ingroup QxModelView
  70. * \brief qx::IxModel : interface to manage Qt model/view architecture with classes registered into QxOrm context (Qt widgets and/or QML views)
  71. *
  72. * <b>QxModelView</b> module provides an easy way to work with Qt model/view engine with all classes registered into QxOrm context :
  73. * - Qt widgets : QTableView or QListView for example to display/modify a database table content ;
  74. * - QML : each property defined in QxOrm context is exposed to QML engine : QxModelView module makes easier integration between QML and databases.
  75. *
  76. * qx::IxModel interface provides a generic way for all models linked to persistents classes registered into QxOrm context. All methods of this class prefixed by <i>qx</i> call functions from qx::dao namespace and then communicate with database.
  77. *
  78. * The <i>qxBlogModelView</i> sample project in <i>./test/</i> directory of QxOrm package shows how to create quickly a model and associate it to the Qt model/view engine (first with a Qt widget, then with a QML view).
  79. *
  80. * 1- Here is an example to display/modify data from 'author' table (go to qxBlog tutorial for 'author' class definition) in a QTableView :
  81. * \code
  82. // Create a model and fetch all data from database
  83. qx::IxModel * pModel = new qx::QxModel<author>();
  84. pModel->qxFetchAll();
  85. // Associate the model to a QTableView and display it
  86. QTableView tableView;
  87. tableView.setModel(pModel);
  88. tableView.show();
  89. * \endcode
  90. *
  91. * 2- Here is another example in QML (with Qt5, QxModelView module works fine with Qt4 too) :
  92. * \code
  93. // Create a model and fetch all data from database
  94. qx::IxModel * pModel = new qx::QxModel<author>();
  95. pModel->qxFetchAll();
  96. // Associate the model to a QML view and display it
  97. QQuickView qmlView;
  98. qmlView.rootContext()->setContextProperty("myModel", pModel);
  99. qmlView.setSource(QUrl("qrc:/documents/main.qml"));
  100. qmlView.show();
  101. * \endcode
  102. *
  103. * And here is the 'main.qml' file content :
  104. * \code
  105. import QtQuick 2.1
  106. import QtQuick.Controls 1.0
  107. Item {
  108. width: 400
  109. height: 300
  110. Row {
  111. height: 20
  112. spacing: 20
  113. Button {
  114. text: "Clear"
  115. onClicked: myModel.clear()
  116. }
  117. Button {
  118. text: "Fetch All"
  119. onClicked: myModel.qxFetchAll_()
  120. }
  121. Button {
  122. text: "Save"
  123. onClicked: myModel.qxSave_()
  124. }
  125. }
  126. ListView {
  127. y: 30
  128. height: 270
  129. model: myModel
  130. delegate: Row {
  131. height: 20
  132. spacing: 10
  133. Text { text: "id: " + author_id }
  134. TextField {
  135. text: name
  136. onTextChanged: name = text
  137. }
  138. }
  139. }
  140. }
  141. * \endcode
  142. *
  143. * As you can see in the 'main.qml' file, 'author_id' and 'name' properties of 'author' model ('myModel' variable) can be automatically read and write (because they are registered into QxOrm context).
  144. * Moreover, qx::IxModel interface provides a list of methods for QML side (Q_INVOKABLE) to communicate with database : for example, the 'Save' button will save the model in database without having to write a C++ function.
  145. *
  146. * <b>Note :</b> a <b>QxEntityEditor</b> plugin generates automatically the code to manage models with relationships. Then it is possible to work with nested C++ models.
  147. */
  148. class QX_DLL_EXPORT IxModel : public QAbstractItemModel
  149. {
  150. Q_OBJECT
  151. template <typename U, typename V> friend struct qx::model_view::detail::QxNestedModel;
  152. template <typename U, typename V> friend struct qx::model_view::detail::QxNestedModel_Generic;
  153. template <typename U, typename V> friend struct qx::model_view::detail::QxNestedModel_Container;
  154. public:
  155. enum e_auto_update_database { e_no_auto_update, e_auto_update_on_field_change };
  156. typedef QHash<QString, IxModel *> type_relation_by_name;
  157. typedef QList<type_relation_by_name> type_lst_relation_by_name;
  158. typedef QHash<IxModel *, QPair<int, QString> > type_child_to_its_relation;
  159. protected:
  160. IxClass * m_pClass; //!< Class introspection registered into QxOrm context associated to the model
  161. IxClass * m_pModelClass; //!< If model itself is registered into QxOrm context, then you can use this property to work with introspection engine
  162. IxDataMemberX * m_pDataMemberX; //!< List of properties defined into QxOrm context
  163. IxDataMember * m_pDataMemberId; //!< Primary key (property id) defined into QxOrm context
  164. IxCollection * m_pCollection; //!< Interface to store a list of items
  165. QHash<int, QByteArray> m_lstRoleNames; //!< List of model's role names to expose data to QML
  166. QList<IxDataMember *> m_lstDataMember; //!< List of data member exposed by the model
  167. QHash<QString, int> m_lstDataMemberByKey; //!< List of data member key to get column index in model
  168. QHash<QString, QVariant> m_lstHeadersData; //!< List of headers data by role and data member key
  169. QStringList m_lstColumns; //!< List of columns exposed by the model (if empty, all columns)
  170. QSqlDatabase m_database; //!< Database connexion to execute SQL queries (if empty, default database connexion)
  171. QSqlError m_lastError; //!< Last SQL error
  172. IxModel * m_pParent; //!< Parent model, NULL if current model is the root model
  173. type_lst_relation_by_name m_lstChild; //!< List of child model : QxEntityEditor uses this property to manage relationships and create complex data structure
  174. type_child_to_its_relation m_hChild; //!< Reverse link to m_lstChild, used in setData() to save relations
  175. e_auto_update_database m_eAutoUpdateDatabase; //!< Auto-update database on field change (detected by the setData() method)
  176. IxDataMember * m_pDataMemberRelationToParent; //!< The data member holding relationship to its parent model (if one exists), used only by nested models
  177. long m_lManualInsertIndex; //!< Index to insert manually items to the collection
  178. QHash<QString, QVariant> m_hCustomProperties; //!< Use this generic hash-table to define extra-properties in your custom classes which inherit from qx::IxModel interface (instead of creating new properties) ==> this will ensure that sizeof(qx::IxModel) == sizeof(YourCustomClass), this is important with nested models feature
  179. public:
  180. IxModel(QObject * parent = 0);
  181. virtual ~IxModel();
  182. IxClass * getClass() const;
  183. IxClass * getModelClass() const;
  184. IxCollection * getCollection() const;
  185. QSqlDatabase getDatabase() const;
  186. QSqlError getLastError() const;
  187. Q_INVOKABLE QString getLastErrorAsString() const;
  188. Q_INVOKABLE QStringList getListOfColumns() const;
  189. QHash<QString, QString> getListOfHeaders() const; //!< Obsolete : use headerData() instead
  190. IxDataMember * getDataMember(int column) const;
  191. Q_INVOKABLE QString getDataMemberKey(int column) const;
  192. Q_INVOKABLE int getRowCount() const;
  193. Q_INVOKABLE QVariant getModelValue(int row, const QString & column) const;
  194. Q_INVOKABLE int getColumnIndex(const QString & sColumnName) const;
  195. Q_INVOKABLE int getAutoUpdateDatabase_() const;
  196. e_auto_update_database getAutoUpdateDatabase() const;
  197. Q_INVOKABLE virtual bool getShowEmptyLine() const = 0; //!< Can be useful when a model is displayed in a table (QTableView for example) to add automatically an empty row at the end of the table to insert quickly new items (for example, same style like QxEntityEditor list of properties/relationships)
  198. Q_INVOKABLE QVariant getCustomProperty(const QString & key) const;
  199. Q_INVOKABLE QObject * getParentModel() const; //!< Can be used to figure out whether this model has a parent model (used only by nested models)
  200. Q_INVOKABLE void dumpModel(bool bJsonFormat = true) const;
  201. Q_INVOKABLE QObject * cloneModel();
  202. void setDatabase(const QSqlDatabase & db);
  203. Q_INVOKABLE void setListOfColumns(const QStringList & lst);
  204. void setListOfHeaders(const QHash<QString, QString> & lst); //!< Obsolete : use setHeaderData() instead
  205. Q_INVOKABLE bool setModelValue(int row, const QString & column, const QVariant & value);
  206. void setParentModel(IxModel * pParent);
  207. Q_INVOKABLE void setAutoUpdateDatabase_(int i);
  208. void setAutoUpdateDatabase(e_auto_update_database e);
  209. Q_INVOKABLE virtual void setShowEmptyLine(bool b) = 0;
  210. Q_INVOKABLE void setCustomProperty(const QString & key, const QVariant & val);
  211. Q_INVOKABLE QString toJson(int row = -1) const; //!< On QML side, use JSON.parse() to create a javascript object after calling this qx::IxModel::toJson() method
  212. Q_INVOKABLE bool fromJson(const QString & json, int row = -1); //!< On QML side, use JSON.stringify() on a javascript object before calling this qx::IxModel::fromJson() method
  213. Q_INVOKABLE QVariant getRelationshipValues(int row, const QString & relation, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()); //!< Depending on relationship type (1-1, 1-n, n-1, n-n) : can return a QVariantMap type or a QVariantList type
  214. Q_INVOKABLE bool setRelationshipValues(int row, const QString & relation, const QVariant & values); //!< Depending on relationship type (1-1, 1-n, n-1, n-n) : values parameter can be a QVariantMap type or a QVariantList type
  215. virtual long qxCount(const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL) = 0;
  216. virtual QSqlError qxCount(long & lCount, const qx::QxSqlQuery & query = qx::QxSqlQuery(), QSqlDatabase * pDatabase = NULL) = 0;
  217. virtual QSqlError qxFetchById(const QVariant & id, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  218. virtual QSqlError qxFetchAll(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  219. virtual QSqlError qxFetchByQuery(const qx::QxSqlQuery & query, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  220. virtual QSqlError qxFetchRow(int row, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  221. virtual QSqlError qxInsert(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  222. virtual QSqlError qxInsertRow(int row, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  223. virtual QSqlError qxUpdate(const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  224. virtual QSqlError qxUpdateRow(int row, const qx::QxSqlQuery & query = qx::QxSqlQuery(), const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  225. virtual QSqlError qxSave(const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  226. virtual QSqlError qxSaveRow(int row, const QStringList & relation = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  227. virtual QSqlError qxSaveRowData(int row, const QStringList & column = QStringList(), QSqlDatabase * pDatabase = NULL) = 0;
  228. virtual QSqlError qxDeleteById(const QVariant & id, QSqlDatabase * pDatabase = NULL) = 0;
  229. virtual QSqlError qxDeleteAll(QSqlDatabase * pDatabase = NULL) = 0;
  230. virtual QSqlError qxDeleteByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL) = 0;
  231. virtual QSqlError qxDeleteRow(int row, QSqlDatabase * pDatabase = NULL) = 0;
  232. virtual QSqlError qxDestroyById(const QVariant & id, QSqlDatabase * pDatabase = NULL) = 0;
  233. virtual QSqlError qxDestroyAll(QSqlDatabase * pDatabase = NULL) = 0;
  234. virtual QSqlError qxDestroyByQuery(const qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL) = 0;
  235. virtual QSqlError qxDestroyRow(int row, QSqlDatabase * pDatabase = NULL) = 0;
  236. virtual QSqlError qxExecuteQuery(qx::QxSqlQuery & query, QSqlDatabase * pDatabase = NULL) = 0;
  237. virtual qx_bool qxExist(const QVariant & id, QSqlDatabase * pDatabase = NULL) = 0;
  238. virtual qx::QxInvalidValueX qxValidate(const QStringList & groups = QStringList()) = 0;
  239. virtual qx::QxInvalidValueX qxValidateRow(int row, const QStringList & groups = QStringList()) = 0;
  240. Q_INVOKABLE int qxCount_(const QString & sQuery);
  241. Q_INVOKABLE bool qxFetchById_(const QVariant & id, const QStringList & relation = QStringList());
  242. Q_INVOKABLE bool qxFetchAll_(const QStringList & relation = QStringList());
  243. Q_INVOKABLE bool qxFetchByQuery_(const QString & sQuery, const QStringList & relation = QStringList());
  244. Q_INVOKABLE bool qxFetchRow_(int row, const QStringList & relation = QStringList());
  245. Q_INVOKABLE bool qxInsert_(const QStringList & relation = QStringList());
  246. Q_INVOKABLE bool qxInsertRow_(int row, const QStringList & relation = QStringList());
  247. Q_INVOKABLE bool qxUpdate_(const QString & sQuery, const QStringList & relation = QStringList());
  248. Q_INVOKABLE bool qxUpdateRow_(int row, const QString & sQuery, const QStringList & relation = QStringList());
  249. Q_INVOKABLE bool qxSave_(const QStringList & relation = QStringList());
  250. Q_INVOKABLE bool qxSaveRow_(int row, const QStringList & relation = QStringList());
  251. Q_INVOKABLE bool qxDeleteById_(const QVariant & id);
  252. Q_INVOKABLE bool qxDeleteAll_();
  253. Q_INVOKABLE bool qxDeleteByQuery_(const QString & sQuery);
  254. Q_INVOKABLE bool qxDeleteRow_(int row);
  255. Q_INVOKABLE bool qxDestroyById_(const QVariant & id);
  256. Q_INVOKABLE bool qxDestroyAll_();
  257. Q_INVOKABLE bool qxDestroyByQuery_(const QString & sQuery);
  258. Q_INVOKABLE bool qxDestroyRow_(int row);
  259. Q_INVOKABLE bool qxExecuteQuery_(const QString & sQuery);
  260. Q_INVOKABLE bool qxExist_(const QVariant & id);
  261. Q_INVOKABLE QString qxValidate_(const QStringList & groups = QStringList());
  262. Q_INVOKABLE QString qxValidateRow_(int row, const QStringList & groups = QStringList());
  263. QSqlError saveChildRelations(IxModel * pChild);
  264. QVariant getIdFromChild(IxModel * pChild) const; //!< Used to save foreign key in a nested model
  265. QPair<int, QString> getChildPosition(IxModel * pChild) const;
  266. protected:
  267. void raiseEvent_headerDataChanged(Qt::Orientation orientation, int first, int last);
  268. #if (QT_VERSION >= 0x050000)
  269. void raiseEvent_dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector<int> & roles = QVector<int>());
  270. void raiseEvent_layoutAboutToBeChanged(const QList<QPersistentModelIndex> & parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
  271. void raiseEvent_layoutChanged(const QList<QPersistentModelIndex> & parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
  272. #else // (QT_VERSION >= 0x050000)
  273. void raiseEvent_dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
  274. void raiseEvent_layoutAboutToBeChanged();
  275. void raiseEvent_layoutChanged();
  276. #endif // (QT_VERSION >= 0x050000)
  277. public:
  278. Q_INVOKABLE void clear(bool bUpdateColumns = false);
  279. virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
  280. virtual bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
  281. virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
  282. virtual int columnCount(const QModelIndex & parent = QModelIndex()) const;
  283. virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
  284. virtual QModelIndex parent(const QModelIndex & index) const;
  285. virtual bool hasChildren(const QModelIndex & parent = QModelIndex()) const;
  286. virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
  287. virtual Qt::ItemFlags flags(const QModelIndex & index) const;
  288. virtual Qt::DropActions supportedDropActions() const;
  289. virtual bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex());
  290. virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant & value, int role = Qt::EditRole);
  291. bool setHeaderData(const QString & sColumnName, const QVariant & value, int role = Qt::EditRole);
  292. #if (QT_VERSION >= 0x050000)
  293. virtual QHash<int, QByteArray> roleNames() const;
  294. virtual Qt::DropActions supportedDragActions() const;
  295. #endif // (QT_VERSION >= 0x050000)
  296. protected:
  297. virtual QObject * cloneModelImpl() = 0;
  298. virtual void dumpModelImpl(bool bJsonFormat) const = 0;
  299. virtual void * getRowItemAsVoidPtr(int row) const = 0;
  300. virtual bool isDirtyRow(int row) const = 0;
  301. virtual void insertDirtyRowToModel() = 0;
  302. virtual void updateShowEmptyLine() = 0;
  303. virtual void syncNestedModel(int row, const QStringList & relation);
  304. virtual void syncAllNestedModel(const QStringList & relation);
  305. void syncNestedModelRecursive(IxModel * pNestedModel, const QStringList & relation);
  306. void generateRoleNames();
  307. QSqlDatabase * database(QSqlDatabase * other);
  308. IxModel * getChild(long row, const QString & relation);
  309. void insertChild(long row, const QString & relation, IxModel * pChild);
  310. void removeListOfChild(long row);
  311. bool removeRowsGeneric(int row, int count);
  312. bool removeRowsAutoUpdateOnFieldChange(int row, int count);
  313. #ifndef _QX_NO_JSON
  314. virtual QString toJson_Helper(int row) const = 0;
  315. virtual bool fromJson_Helper(const QString & json, int row) = 0;
  316. virtual QVariant getRelationshipValues_Helper(int row, const QString & relation, bool bLoadFromDatabase, const QString & sAppendRelations) = 0;
  317. virtual bool setRelationshipValues_Helper(int row, const QString & relation, const QVariant & values) = 0;
  318. #endif // _QX_NO_JSON
  319. };
  320. } // namespace qx
  321. #endif // _IX_MODEL_H_