The ObjectModel class
Writing insert() and update() by hand for every table is tedious, and it is easy to forget to check the values. ObjectModel is a core base class that represents one table row as an object: fields become properties, and the class takes care of saving, validating and protecting values. Products, orders and customers in the core itself are built the same way.
Where the class lives
Module models are kept in the classes folder. Create the file by hand:
modules/acmehello/
├── acmehello.php
├── classes/
│ └── AcmeReview.php
├── upgrade/
└── views/Describing the model
<?php
// modules/acmehello/classes/AcmeReview.php
class AcmeReview extends ObjectModel
{
public $id_product;
public $id_customer;
public $author;
public $rating = 5;
public $content;
public $date_add;
public static $definition = [
'table' => 'acmehello_review',
'primary' => 'id_review',
'fields' => [
'id_product' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true],
'id_customer' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedId'],
'author' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true, 'size' => 100],
'rating' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
'content' => ['type' => self::TYPE_STRING, 'validate' => 'isMessage'],
'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate'],
],
];
}The public properties mirror the table columns. The $definition array names the table without the prefix, the primary key and the fields. The class name starts with the module prefix so that it does not clash with a core class.
Loading the file
The engine does not know about the file until it is included. Add a line at the top of the main file, after the _PS_VERSION_ check:
// modules/acmehello/acmehello.php
require_once __DIR__ . '/classes/AcmeReview.php';Now the AcmeReview class is available wherever the module is loaded.
Field description keys
| Key | What it sets |
|---|---|
type | The type: TYPE_INT, TYPE_BOOL, TYPE_STRING, TYPE_FLOAT, TYPE_DATE, TYPE_HTML |
validate | The name of a Validate class method used for the check |
required | The field is mandatory |
size | The maximum string length |
Creating a record
Rewrite the addReview() method of the main class to use the model:
// modules/acmehello/acmehello.php
public function addReview(int $productId, string $author, int $rating, string $content): bool
{
$review = new AcmeReview();
$review->id_product = $productId;
$review->id_customer = (int) $this->context->customer->id;
$review->author = $author;
$review->rating = $rating;
$review->content = $content;
return $review->add();
}add() checks the fields against $definition, escapes the values and runs the insert. The date_add field is filled with the current time automatically. After the insert the id of the new row is in $review->id.
Loading, changing, deleting
$review = new AcmeReview(7);
if (Validate::isLoadedObject($review)) {
$review->rating = 4;
$review->update();
}
$review->delete();An id in the constructor loads the row from the database. If there is no row with that id, the object is created empty — that is why the result is checked with Validate::isLoadedObject().
| Method | What it does |
|---|---|
add() | Inserts a new row |
update() | Saves changes |
save() | Chooses add() or update() itself |
delete() | Deletes the row |
validateFields(false) | Checks the fields, returns true or false |
Validation errors
If a value fails the check, add() and update() throw a PrestaShopException. User data is checked beforehand:
if (!$review->validateFields(false)) {
return false;
}With the false argument the method does not throw but returns the result.
Lists still go through DbQuery
A model works with one row. The list of a product's reviews is still fetched with the getReviews() method from the previous lesson — a DbQuery query.
Common mistake
A field is added to $definition, but a public property with the same name is not declared in the class, or the other way round. The value is silently not saved or not loaded. Every column must be in three places: in the table, in the class properties and in fields.