Defining data classes
We are building our gallery from the ground up. We will start with the implementation of our data classes to be able to properly write the database layer. The application aims to organize pictures into albums. Hence, the two obvious classes are Album and Picture. In our example, an album simply has a name. A Picture class must belong to an Album class and has a file path (the path on your filesystem where the original file is located).
The Album class has already been created on project creation. Open the Album.h file and update it to include the following implementation:
#include <QString> #include "gallery-core_global.h" class GALLERYCORESHARED_EXPORT Album { public: explicit Album(const QString& name = ""); int id() const; void setId(int id); QString name() const; void setName(const QString& name); private: int mId; QString mName; };
As you can see, the Album class contains only a mId variable (the database ID) and a mName variable. In a typical OOP (Object Oriented Paradigm) fashion, the Album class would have had a QVector<Picture>mPictures field. We did not do it on purpose. By decoupling these two objects, we will have more flexibility when we want to load an album without pulling the potential thousands of associated pictures. The other problem in having mPictures in the Album class is that the developer (you or anybody else) using this code will ask himself: when is mPictures loaded? Should I do a partial load of the Album and have an incomplete Album or should I always load Album with every picture in it?
By completely removing the field, the question ceases to exist, and the code is simpler to grasp. The developer knows intuitively that he will have to explicitly load the pictures if he want them; otherwise, he can continue with this simple Album class.
The getters and setters are obvious enough; we will let you implement them without showing them to you. We will only take a look at the Album class' constructor in Album.cpp:
Album::Album(const QString& name) : mId(-1), mName(name) { }
The mId variable is initialized to -1 to be sure that, by default, an invalid id is used, and the mName variable is assigned a name value.
We can now proceed to the Picture class. Create a new C++ class named Picture and open Picture.h to modify it like so:
#include <QUrl> #include <QString> #include "gallery-core_global.h" class GALLERYCORESHARED_EXPORT Picture { public: Picture(const QString& filePath = ""); Picture(const QUrl& fileUrl); int id() const; void setId(int id); int albumId() const; void setAlbumId(int albumId); QUrl fileUrl() const; void setFileUrl(const QUrl& fileUrl); private: int mId; int mAlbumId; QUrl mFileUrl; };
Do not forget to add the GALLERYCORESHARED_EXPORT macro right before the class keyword to export the class from the library. As a data structure, Picture has a mId variable, belongs to a mAlbumId variable, and has a mUrl value. We use the QUrl type to make path manipulation easier to use depending on the platform (desktop or mobile).
Let's take a look at Picture.cpp:
#include "Picture.h" Picture::Picture(const QString& filePath) : Picture(QUrl::fromLocalFile(filePath)) { } Picture::Picture(const QUrl& fileUrl) : mId(-1), mAlbumId(-1), mFileUrl(fileUrl) { } QUrl Picture::fileUrl() const { return mFileUrl; } void Picture::setFileUrl(const QUrl& fileUrl) { mFileUrl = fileUrl; }
In the first constructor, the static function, QUrl::fromLocalFile, is called to provide a QUrl object to the other constructor, which takes a QUrl parameter.
The ability to call other constructors is a nice addition in C++11.