ObjectModel class is one of the main pillars of PrestaShop’s legacy core. While a complete migration to Symfony/Doctrine entities is planned in the roadmap, ObjectModel will remain present and available in our software for a while.
ObjectModel is the Data Access Layer for PrestaShop, implemented following the Active Record pattern. The Data Access Layer (DAL) is a part (with the Database Abstraction Layer - DBAL) of the Object Relational Mapping (ORM) legacy system for PrestaShop.
Read more about Object relation mapping (ORM), Database abstraction layer (DBAL), Data access layer (DAL), and Active Record:
A class extending the ObjectModel class is tied to a database table. Its static attribute ($definition
) represents the model.
Its instances are tied to database records.
When instantiated with an $id
in the class constructor, the attributes are retrieved from the related database record (using the $id
as the primary key to find the table record).
$definition
model can break the entire system or lead to data loss.You can create a new entity (in a module for example), with its own database table, managed by ObjectModel.
To do this, create class extending the ObjectModel:
class Cms extends ObjectModel
{
}
Next, define the properties of your entity :
class Cms extends ObjectModel
{
public $id_cms;
public $id_cms_category;
public $position;
public $active;
[...]
}
The next step is defining the model.
To define your model (reflection of the database table structure, fields, type, …), you must use the $definition
static variable.
For instance:
/**
* Example from the CMS model (CMSCore)
*/
public static $definition = [
'table' => 'cms',
'primary' => 'id_cms',
'multilang' => true,
'fields' => array(
'id_cms_category' => ['type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'],
'position' => ['type' => self::TYPE_INT],
'active' => ['type' => self::TYPE_BOOL],
// Language fields
'meta_description' => [
'type' => self::TYPE_STRING,
'lang' => true,
'validate' => 'isGenericName',
'size' => 255
],
'meta_keywords' => [
'type' => self::TYPE_STRING,
'lang' => true,
'validate' => 'isGenericName',
'size' => 255
],
'meta_title' => [
'type' => self::TYPE_STRING,
'lang' => true,
'validate' => 'isGenericName',
'required' => true,
'size' => 128
],
'link_rewrite' => [
'type' => self::TYPE_STRING,
'lang' => true,
'validate' => 'isLinkRewrite',
'required' => true,
'size' => 128
],
'content' => [
'type' => self::TYPE_HTML,
'lang' => true,
'validate' => 'isString',
'size' => 3999999999999
],
)
];
Let’s analyse this definition:
public static $definition = [
'table' => 'cms',
'primary' => 'id_cms',
'multilang' => true,
'fields' => array(
table
is the related database table name (without the database table PREFIX
),primary
is the name of the PRIMARY KEY
field in the database table, which will be used as $id
in the ObjectModelmultilang
is a boolean value indicating that the entity is available in multiple langages, see Multiple languagesfields
is an array containing all other of the fields from the database table.A field is defined by a key (its name in the database table) and an array of its settings.
'meta_description' => [
'type' => self::TYPE_STRING,
'lang' => true,
'validate' => 'isGenericName',
'size' => 255
],
In this example:
meta_description
is the field’s nameself::TYPE_STRING
is its typelang
is a boolean related to multilang features (Multiple languages)validate
is a validation rule (optional)size
is the max size of the field. It should match the database definition to avoid data cropping or overflow when inserting/updating objects.Field type is an important setting, it determines how ObjectModel will format your data.
Type in ObjectModel | Related type in MySQL | Formating |
---|---|---|
TYPE_INT | INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT, … | Cast to int |
TYPE_BOOL | SMALLINT | Cast to int |
TYPE_STRING | VARCHAR | Return string, escape value, remove php and html tags |
TYPE_FLOAT | FLOAT, DOUBLE | Cast to float |
TYPE_DATE | DATE | Return string, escape value, remove php and html tags |
TYPE_HTML | BLOB, TEXT | Return string, escape value, keep safe html tags |
TYPE_NOTHING | Use with caution, not secure, does no formating | |
TYPE_SQL | BLOB, TEXT | Return string, escape value, keep safe html tag |
Several validation rules are available for your ObjectModel fields. Please refer to the Validate class of PrestaShop for a complete list.
ObjectModel has a mechanism to handle creation/modification timestamps. To use this feature, you have to define those two properties of your entity and set them up in the model definition.
class Cms extends ObjectModel
{
[...]
public $date_add;
public $date_upd;
public static $definition = [
[...]
'fields' => array(
[...]
'date_add' => ['type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDate'],
'date_upd' => ['type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDate']
)
];
}
$cms = new Cms();
$cms->position = 2;
...
$cms->save();
In this example, we create an entity from scratch. Then, we set its position
attribute, and we call the save()
method. The save()
method will trigger the add()
method since its id
attribute is not yet known (because the entity is not created in database).
If the insert is successful, the ObjectModel class will set the entity’s id (retrieved from the database). Complete reference here.
$id = 2; // id of the object in database
$cms = new Cms($id);
$cms->position = 3;
...
$cms->save();
In this example, we retrieve an entity from the database with its id. Then, we change its position
attribute and call the same save()
method. The save()
method will trigger the update()
method and not the add()
method since its id
attribute is known. Complete reference here.
Two delete mechanisms are available with ObjectModel: hard delete and soft delete. Hard-delete deletes the record from the database, while soft-delete sets a flag in the table’s field indicating that this record is deleted.
Soft delete is not always available.
If the model object doesn’t have a deleted
property or there is no deleted
field available in the class definition, a PrestaShopException
will be thrown
Soft delete does not trigger DeleteHooks.
Soft deleting an object does not trigger Delete related hooks, but will trigger Update related hooks. ObjectModel lifecycle hooks
$id = 2; // id of the object in database
$cms = new Cms($id);
$cms->softDelete(); // sets the deleted property to true, and triggers an update() call
...
$cms->delete(); // triggers a DELETE statement to the DBAL
PrestaShop’s ObjectModel can handle translations (also called internationalization, or i18n) of your objects.
When declaring a multi-language ObjectModel, PrestaShop will fetch another database table named like your base database table, but with a suffix _lang
This table references the id of the base Object (id_cms
), the id of the language (id_lang
), and each translatable field.
In our previous example, for Cms
ObjectModel:
To do so, you must declare the multilang
setting of your model definition to true
:
public static $definition = [
...
'multilang' => true,
...
And then, you must declare which fields are available for translations:
'fields' => array(
...
'meta_description' => [
...
'lang' => true,
...
],
...
)
Translatable fields are available in your ObjectModel as array
.
In our example, to update the attributes meta_title
for languages EN ($lang_id=1
) and FR ($lang_id=2
), use the following method :
$cms->meta_title[1] = "My awesome title";
$cms->meta_title[2] = "Mon fabuleux titre";
$cms->save();
But… what if i want to retrieve my object only for a specific language, and update its meta_title
?
In this case, you can load your object with the $id_lang
parameter in constructor, and you will have a non-array accessor :
$cms = new Cms($cms_id, $lang_id);
$cms->meta_title = "Mon fabuleux titre";
$cms->save();
PrestaShop’s ObjectModel can handle multiple stores (or multi shop) ObjectModels.
When declaring a multi-store ObjectModel, PrestaShop will fetch another database table named like your base database table, with a suffix _shop
This table is a pivot table referencing at least the id of the base Object (id_cms
) and the id of the shop (id_shop
).
In our previous example, for Cms
ObjectModel:
To do so, you must declare the multishop
setting of your model definition to true
:
public static $definition = [
...
'multishop' => true,
...
You can associate an object to one or several stores with the associateTo
method:
$cms->associateTo(1); // associates the object to the store #1
...
$cms->associateTo([1, 2, 4]); // associates the object to the stores #1, #2 and #4
PrestaShop’s ObjectModel can handle both multiple languages and multiple shop entities. The entity Category
is a good example of this case:
When declaring a multi-store ObjectModel, PrestaShop will fetch another database table named like your base database table, with a suffix _shop
This table is a pivot table referencing at least the id of the base Object (id_cms
) and the id of the shop (id_shop
).
In our previous example, for Category
ObjectModel :
To do so, you must declare the multilang_shop
setting of your model definition to true
:
public static $definition = [
...
'multilang_shop' => true,
...
And then, you must declare which fields are available for translations:
'fields' => array(
...
'additional_description' => [
...
'lang' => true,
...
],
...
)
While languages are accessible with accessors, if you need to programmatically retrieve an ObjectModel related to a particular shop (not the selected / current shop from Context), you need to change the method used to load an object:
$targetShopId = 2;
$category = new Category(1, null, $targetShopId); // 1 is the id of the object
If you need to update a translatable field on your entity, you need to add a Shop::setContext()
call before you save your object :
$targetShopId = 2;
Shop::setContext(Shop::CONTEXT_SHOP, $targetShopId);
$category = new Category(1, null, $targetShopId);
$category->additional_description[1] = "Additional description for shop #2"; // language id #1, english
$category->additional_description[2] = "Description additionelle pour le shop #2"; // language id #2, french
$category->save();
To duplicate an object, use the following method : duplicateObject()
$cms = new Cms(2);
$duplicatedCms = $cms->duplicateObject();
duplicateObject will save the object to database.
Please note that the duplicateObject()
method will instantly save the duplicated object to the database.
Since
8.x
, a partial update mechanism is available in ObjectModel. This mechanism allows you to choose which attributes you want to update during the update()
method call.
On previous versions ( 1.7.x , 1.6.x , …), this method was already available but was not working properly.
Example:
$cms = new Cms(2);
$cms->position = 4;
$cms->active = 0;
$cms->setFieldsToUpdate(["position" => true]);
$cms->save();
In this example, only the position
is updated, active
(and all other fields) will not be updated in the database.
You need to specify the language Ids you want to update, as an array :
$cms = new Cms(2);
$cms->meta_title[1] = "My awesome title"; // language id #1
$cms->meta_title[2] = "Mon fabuleux titre"; // language id #2
$cms->setFieldsToUpdate(
[
"meta_title" => [
1 => true,
2 => false
]
]
);
$cms->save(); // only meta_title for language id #1 will be updated
A mecanism of state is available with ObjectModel : active / inactive state. When triggered, this mecanism allows your entities to be enabled / disabled.
Status is not always available.
If the model object has no active
property or no active
definition field, a PrestaShopException
will be thrown
$id = 2; // id of the entity in database
$cms = new Cms($id);
$cms->toggleStatus(); // sets the active property to true or false (depending on its current value), and triggers an update() call
You can delete multiple object at once with the deleteSelection
method. Pass an array of IDs to delete to this method, and they will be deleted.
Usage :
$cmsIdsToDelete = [1, 2, 3, 8, 10];
(new Cms())->deleteSelection($cmsIdsToDelete);
Thanks to the hooks, you can alter the Object Model or execute functions during the lifecycle of your models. Every hook receive an instance of the manipulated object model:
As an example, this is how you can retrieve information about a product when we delete it from the database:
use Product;
// In a module
public function hookActionObjectProductDeleteAfter(Product $product)
{
PrestaShopLogger::addLog(
sprintf('Product with id %s was deleted with success', $product->id_product)
);
}
Here is the reference of the methods described on this page. Many other methods that we don’t described here are available.
See complete implementation here : ObjectModel.php
/**
* Builds the object.
*
* @param int|null $id if specified, loads and existing object from DB (optional)
* @param int|null $id_lang required if object is multilingual (optional)
* @param int|null $id_shop ID shop for objects with multishop tables
* @param TranslatorComponent|null $translator
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function __construct($id = null, $id_lang = null, $id_shop = null, $translator = null)
/**
* Saves current object to database (add or update).
*
* @param bool $null_values
* @param bool $auto_date
*
* @return bool Insertion result
*
* @throws PrestaShopException
*/
public function save($null_values = false, $auto_date = true)
/**
* Takes current object ID, gets its values from database,
* saves them in a new row and loads newly saved values as a new object.
*
* @return ObjectModel|false
*
* @throws PrestaShopDatabaseException
*/
public function duplicateObject()
/**
* Deletes current object from database.
*
* @return bool True if delete was successful
*
* @throws PrestaShopException
*/
public function delete()
/**
* Deletes multiple objects from the database at once.
*
* @param array $ids array of objects IDs
*
* @return bool
*/
public function deleteSelection($ids)
/**
* Does a soft delete on current object, using the "deleted" field in DB
* If the model object has no "deleted" property or no "deleted" definition field it will throw an exception
*
* @return bool
*
* @throws PrestaShopDatabaseException
* @throws PrestaShopException
*/
public function softDelete()
/**
* Toggles object status in database.
*
* @return bool Update result
*
* @throws PrestaShopException
*/
public function toggleStatus()
/**
* This function associate an item to its context.
*
* @param int|array $id_shops
*
* @return bool|void
*
* @throws PrestaShopDatabaseException
*/
public function associateTo($id_shops)
/**
* Set a list of specific fields to update
* array(field1 => true, field2 => false,
* langfield1 => array(1 => true, 2 => false)).
*
* @since 1.5.0.1
*
* @param array<string, bool|array<int, bool>>|null $fields
*/
public function setFieldsToUpdate(?array $fields)
{
$this->update_fields = $fields;
}